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 is implemented in terms of two new expose schemas in
entity-manager:

- MCTPInterface
- MCTPDevice

mctpreactor tracks instances of both appearing as a result of inventory
changes, and uses the provided information to dynamically configure the
endpoints via mctpd. This dynamic configuration may include assignment
of static endpoint IDs to the devices as they appear.

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 Feb 28, 2024
1 parent 43847a8 commit b9bb35d
Show file tree
Hide file tree
Showing 8 changed files with 1,195 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
86 changes: 86 additions & 0 deletions src/MCTPDeviceRepository.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#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::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 b9bb35d

Please sign in to comment.