Skip to content

Commit

Permalink
Add mctpreactor for dynamic configuration of MCTP networks
Browse files Browse the repository at this point in the history
While mctpd[1] may see heavy use in projects such as OpenBMC, it
implements generic functionality necessary to operate MCTP as a
protocol. It therefore should be easy to use in other contexts, and so
it feels unwise to embed OpenBMC-specific details in its implementation.

Conversely, entity-manager's scope is to expose inventory and board
configuration. It externalises all other responsibilities for the sake
of stability and maintenance. While entity-manager is central to
OpenBMC's implementation and has little use in other contexts, embedding
details of how to configure mctpd in entity-manager exceeds its scope.

Thus we reach the design point of mctpreactor, an intermediary process
that encapsulates OpenBMC-specific and mctpd-specific behaviors to
constrain their dispersion in either direction. The design-point was
reached via discussion at [2].

mctpreactor tracks instances of transport-specific MCTP device
configurations appearing as a result of inventory changes, and uses them
to assign endpoint IDs via mctpd.

The lifecycle of an MCTP device can be quite dynamic - mctpd provides
behaviors to recover[3] or remove endpoints from the network. Their
presence cannot be assumed. mctpreactor handles these events: If
a device is removed at the MCTP layer (as it may be unresponsive),
mctpreactor will periodically attempt to re-establish it as an endpoint
so long as the associated configuration on the entity-manager inventory
object remains exposed.

[1]: https://github.com/CodeConstruct/mctp/
[2]: CodeConstruct/mctp#17
[3]: https://github.com/CodeConstruct/mctp/blob/7ec2f8daa3a8948066390aee621d6afa03f6ecd9/docs/endpoint-recovery.md

Change-Id: I5e362cf6e5ce80ce282bab48d912a1038003e236
Signed-off-by: Andrew Jeffery <[email protected]>
  • Loading branch information
amboar committed Mar 26, 2024
1 parent b2a0f2e commit 92dd7e9
Show file tree
Hide file tree
Showing 13 changed files with 1,527 additions and 0 deletions.
1 change: 1 addition & 0 deletions meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ option('fan', type: 'feature', value: 'enabled', description: 'Enable fan sensor
option('hwmon-temp', type: 'feature', value: 'enabled', description: 'Enable HWMON temperature sensor.',)
option('intrusion', type: 'feature', value: 'enabled', description: 'Enable intrusion sensor.',)
option('ipmb', type: 'feature', value: 'enabled', description: 'Enable IPMB sensor.',)
option('mctp', type: 'feature', value: 'enabled', description: 'Enable MCTP endpoint management')
option('mcu', type: 'feature', value: 'enabled', description: 'Enable MCU sensor.',)
option('nvme', type: 'feature', value: 'enabled', description: 'Enable NVMe sensor.',)
option('psu', type: 'feature', value: 'enabled', description: 'Enable PSU sensor.',)
Expand Down
1 change: 1 addition & 0 deletions service_files/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ unit_files = [
['hwmon-temp', 'xyz.openbmc_project.hwmontempsensor.service'],
['ipmb', 'xyz.openbmc_project.ipmbsensor.service'],
['intrusion', 'xyz.openbmc_project.intrusionsensor.service'],
['mctp', 'xyz.openbmc_project.mctpreactor.service'],
['mcu', 'xyz.openbmc_project.mcutempsensor.service'],
['nvme', 'xyz.openbmc_project.nvmesensor.service'],
['psu', 'xyz.openbmc_project.psusensor.service'],
Expand Down
13 changes: 13 additions & 0 deletions service_files/xyz.openbmc_project.mctpreactor.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=MCTP device configuration
StopWhenUnneeded=false
Requires=xyz.openbmc_project.EntityManager.service
After=xyz.openbmc_project.EntityManager.service

[Service]
Restart=always
RestartSec=5
ExecStart=/usr/bin/mctpreactor

[Install]
WantedBy=multi-user.target
93 changes: 93 additions & 0 deletions src/MCTPDeviceRepository.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#pragma once

#include "MCTPEndpoint.hpp"

class MCTPDeviceRepository
{
private:
// FIXME: Ugh, hack. Figure out a better data structure?
std::map<std::string, std::shared_ptr<MCTPDevice>> devices;

auto lookup(const std::shared_ptr<MCTPDevice>& device)
{
auto pred = [&device](const auto& it) { return it.second == device; };
return std::ranges::find_if(devices, pred);
}

public:
MCTPDeviceRepository() = default;
MCTPDeviceRepository(const MCTPDeviceRepository&) = delete;
MCTPDeviceRepository(MCTPDeviceRepository&&) = delete;
~MCTPDeviceRepository() = default;

MCTPDeviceRepository& operator=(const MCTPDeviceRepository&) = delete;
MCTPDeviceRepository& operator=(MCTPDeviceRepository&&) = delete;

void add(const std::string& inventory,
const std::shared_ptr<MCTPDevice>& device)
{
auto [_, fresh] = devices.emplace(inventory, device);
if (!fresh)
{
throw std::logic_error(
std::format("Tried to add entry for existing device: {}",
device->describe()));
}
}

void remove(const std::shared_ptr<MCTPDevice>& device)
{
auto entry = lookup(device);
if (entry == devices.end())
{
throw std::logic_error(
std::format("Trying to remove unknown device: {}",
entry->second->describe()));
}
devices.erase(entry);
}

void remove(const std::string& inventory)
{
auto entry = devices.find(inventory);
if (entry == devices.end())
{
throw std::logic_error(std::format(
"Trying to remove unknown inventory: {}", inventory));
}
devices.erase(entry);
}

bool contains(const std::string& inventory)
{
return devices.contains(inventory);
}

bool contains(const std::shared_ptr<MCTPDevice>& device)
{
return lookup(device) != devices.end();
}

const std::string& inventoryFor(const std::shared_ptr<MCTPDevice>& device)
{
auto entry = lookup(device);
if (entry == devices.end())
{
throw std::logic_error(
std::format("Cannot retrieve inventory for unknown device: {}",
device->describe()));
}
return entry->first;
}

const std::shared_ptr<MCTPDevice>& deviceFor(const std::string& inventory)
{
auto entry = devices.find(inventory);
if (entry == devices.end())
{
throw std::logic_error(std::format(
"Cannot retrieve device for unknown inventory: {}", inventory));
}
return entry->second;
}
};
Loading

0 comments on commit 92dd7e9

Please sign in to comment.