1275f7c39SAndrew Jeffery #include "MCTPReactor.hpp"
2275f7c39SAndrew Jeffery
3275f7c39SAndrew Jeffery #include "MCTPDeviceRepository.hpp"
4275f7c39SAndrew Jeffery #include "MCTPEndpoint.hpp"
5275f7c39SAndrew Jeffery #include "Utils.hpp"
6275f7c39SAndrew Jeffery
7275f7c39SAndrew Jeffery #include <boost/system/detail/error_code.hpp>
8275f7c39SAndrew Jeffery #include <phosphor-logging/lg2.hpp>
9275f7c39SAndrew Jeffery
10275f7c39SAndrew Jeffery #include <cstdlib>
11275f7c39SAndrew Jeffery #include <memory>
12275f7c39SAndrew Jeffery #include <optional>
13275f7c39SAndrew Jeffery #include <string>
14275f7c39SAndrew Jeffery #include <system_error>
15275f7c39SAndrew Jeffery #include <utility>
16275f7c39SAndrew Jeffery #include <vector>
17275f7c39SAndrew Jeffery
18275f7c39SAndrew Jeffery PHOSPHOR_LOG2_USING;
19275f7c39SAndrew Jeffery
deferSetup(const std::shared_ptr<MCTPDevice> & dev)20275f7c39SAndrew Jeffery void MCTPReactor::deferSetup(const std::shared_ptr<MCTPDevice>& dev)
21275f7c39SAndrew Jeffery {
22275f7c39SAndrew Jeffery debug("Deferring setup for MCTP device at [ {MCTP_DEVICE} ]", "MCTP_DEVICE",
23275f7c39SAndrew Jeffery dev->describe());
24275f7c39SAndrew Jeffery
25275f7c39SAndrew Jeffery deferred.emplace(dev);
26275f7c39SAndrew Jeffery }
27275f7c39SAndrew Jeffery
untrackEndpoint(const std::shared_ptr<MCTPEndpoint> & ep)28275f7c39SAndrew Jeffery void MCTPReactor::untrackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep)
29275f7c39SAndrew Jeffery {
30275f7c39SAndrew Jeffery server.disassociate(MCTPDEndpoint::path(ep));
31275f7c39SAndrew Jeffery }
32275f7c39SAndrew Jeffery
trackEndpoint(const std::shared_ptr<MCTPEndpoint> & ep)33275f7c39SAndrew Jeffery void MCTPReactor::trackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep)
34275f7c39SAndrew Jeffery {
35275f7c39SAndrew Jeffery info("Added MCTP endpoint to device: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
36275f7c39SAndrew Jeffery ep->describe());
37275f7c39SAndrew Jeffery
38275f7c39SAndrew Jeffery ep->subscribe(
39275f7c39SAndrew Jeffery // Degraded
40275f7c39SAndrew Jeffery [](const std::shared_ptr<MCTPEndpoint>& ep) {
41275f7c39SAndrew Jeffery debug("Endpoint entered degraded state: [ {MCTP_ENDPOINT} ]",
42275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe());
43275f7c39SAndrew Jeffery },
44275f7c39SAndrew Jeffery // Available
45275f7c39SAndrew Jeffery [](const std::shared_ptr<MCTPEndpoint>& ep) {
46275f7c39SAndrew Jeffery debug("Endpoint entered available state: [ {MCTP_ENDPOINT} ]",
47275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe());
48275f7c39SAndrew Jeffery },
49275f7c39SAndrew Jeffery // Removed
50275f7c39SAndrew Jeffery [weak{weak_from_this()}](const std::shared_ptr<MCTPEndpoint>& ep) {
51275f7c39SAndrew Jeffery info("Removed MCTP endpoint from device: [ {MCTP_ENDPOINT} ]",
52275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe());
53275f7c39SAndrew Jeffery if (auto self = weak.lock())
54275f7c39SAndrew Jeffery {
55275f7c39SAndrew Jeffery self->untrackEndpoint(ep);
56275f7c39SAndrew Jeffery // Only defer the setup if we know inventory is still present
57275f7c39SAndrew Jeffery if (self->devices.contains(ep->device()))
58275f7c39SAndrew Jeffery {
59275f7c39SAndrew Jeffery self->deferSetup(ep->device());
60275f7c39SAndrew Jeffery }
61275f7c39SAndrew Jeffery }
62275f7c39SAndrew Jeffery else
63275f7c39SAndrew Jeffery {
64275f7c39SAndrew Jeffery info(
65275f7c39SAndrew Jeffery "The reactor object was destroyed concurrent to the removal of the remove match for the endpoint '{MCTP_ENDPOINT}'",
66275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe());
67275f7c39SAndrew Jeffery }
68275f7c39SAndrew Jeffery });
69275f7c39SAndrew Jeffery
70275f7c39SAndrew Jeffery // Proxy-host the association back to the inventory at the same path as the
71275f7c39SAndrew Jeffery // endpoint in mctpd.
72275f7c39SAndrew Jeffery //
73275f7c39SAndrew Jeffery // clang-format off
74275f7c39SAndrew Jeffery // ```
75*19d1fda6SThu Nguyen // # busctl call xyz.openbmc_project.ObjectMapper /xyz/openbmc_project/object_mapper xyz.openbmc_project.ObjectMapper GetAssociatedSubTree ooias /au/com/codeconstruct/mctp1/networks/1/endpoints/9/configured_by / 0 1 xyz.openbmc_project.Configuration.MCTPDevice
76275f7c39SAndrew Jeffery // 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"
77275f7c39SAndrew Jeffery // ```
78275f7c39SAndrew Jeffery // clang-format on
79275f7c39SAndrew Jeffery std::optional<std::string> item = devices.inventoryFor(ep->device());
80275f7c39SAndrew Jeffery if (!item)
81275f7c39SAndrew Jeffery {
82275f7c39SAndrew Jeffery error("Inventory missing for endpoint: [ {MCTP_ENDPOINT} ]",
83275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe());
84275f7c39SAndrew Jeffery return;
85275f7c39SAndrew Jeffery }
86275f7c39SAndrew Jeffery std::vector<Association> associations{
87275f7c39SAndrew Jeffery {"configured_by", "configures", *item}};
88275f7c39SAndrew Jeffery server.associate(MCTPDEndpoint::path(ep), associations);
89275f7c39SAndrew Jeffery }
90275f7c39SAndrew Jeffery
setupEndpoint(const std::shared_ptr<MCTPDevice> & dev)91275f7c39SAndrew Jeffery void MCTPReactor::setupEndpoint(const std::shared_ptr<MCTPDevice>& dev)
92275f7c39SAndrew Jeffery {
93275f7c39SAndrew Jeffery debug(
94275f7c39SAndrew Jeffery "Attempting to setup up MCTP endpoint for device at [ {MCTP_DEVICE} ]",
95275f7c39SAndrew Jeffery "MCTP_DEVICE", dev->describe());
96275f7c39SAndrew Jeffery dev->setup([weak{weak_from_this()},
97275f7c39SAndrew Jeffery dev](const std::error_code& ec,
98275f7c39SAndrew Jeffery const std::shared_ptr<MCTPEndpoint>& ep) mutable {
99275f7c39SAndrew Jeffery auto self = weak.lock();
100275f7c39SAndrew Jeffery if (!self)
101275f7c39SAndrew Jeffery {
102275f7c39SAndrew Jeffery info(
103275f7c39SAndrew Jeffery "The reactor object was destroyed concurrent to the completion of the endpoint setup for '{MCTP_ENDPOINT}'",
104275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe());
105275f7c39SAndrew Jeffery return;
106275f7c39SAndrew Jeffery }
107275f7c39SAndrew Jeffery
108275f7c39SAndrew Jeffery if (ec)
109275f7c39SAndrew Jeffery {
110275f7c39SAndrew Jeffery debug(
111275f7c39SAndrew Jeffery "Setup failed for MCTP device at [ {MCTP_DEVICE} ]: {ERROR_MESSAGE}",
112275f7c39SAndrew Jeffery "MCTP_DEVICE", dev->describe(), "ERROR_MESSAGE", ec.message());
113275f7c39SAndrew Jeffery
114275f7c39SAndrew Jeffery self->deferSetup(dev);
115275f7c39SAndrew Jeffery return;
116275f7c39SAndrew Jeffery }
117275f7c39SAndrew Jeffery
118275f7c39SAndrew Jeffery try
119275f7c39SAndrew Jeffery {
120275f7c39SAndrew Jeffery self->trackEndpoint(ep);
121275f7c39SAndrew Jeffery }
122275f7c39SAndrew Jeffery catch (const MCTPException& e)
123275f7c39SAndrew Jeffery {
124275f7c39SAndrew Jeffery error("Failed to track endpoint '{MCTP_ENDPOINT}': {EXCEPTION}",
125275f7c39SAndrew Jeffery "MCTP_ENDPOINT", ep->describe(), "EXCEPTION", e);
126275f7c39SAndrew Jeffery self->deferSetup(dev);
127275f7c39SAndrew Jeffery }
128275f7c39SAndrew Jeffery });
129275f7c39SAndrew Jeffery }
130275f7c39SAndrew Jeffery
tick()131275f7c39SAndrew Jeffery void MCTPReactor::tick()
132275f7c39SAndrew Jeffery {
133275f7c39SAndrew Jeffery auto toSetup = std::exchange(deferred, {});
134275f7c39SAndrew Jeffery for (const auto& entry : toSetup)
135275f7c39SAndrew Jeffery {
136275f7c39SAndrew Jeffery setupEndpoint(entry);
137275f7c39SAndrew Jeffery }
138275f7c39SAndrew Jeffery }
139275f7c39SAndrew Jeffery
manageMCTPDevice(const std::string & path,const std::shared_ptr<MCTPDevice> & device)140275f7c39SAndrew Jeffery void MCTPReactor::manageMCTPDevice(const std::string& path,
141275f7c39SAndrew Jeffery const std::shared_ptr<MCTPDevice>& device)
142275f7c39SAndrew Jeffery {
143275f7c39SAndrew Jeffery if (!device)
144275f7c39SAndrew Jeffery {
145275f7c39SAndrew Jeffery return;
146275f7c39SAndrew Jeffery }
147275f7c39SAndrew Jeffery
148275f7c39SAndrew Jeffery try
149275f7c39SAndrew Jeffery {
150275f7c39SAndrew Jeffery devices.add(path, device);
151275f7c39SAndrew Jeffery debug("MCTP device inventory added at '{INVENTORY_PATH}'",
152275f7c39SAndrew Jeffery "INVENTORY_PATH", path);
153275f7c39SAndrew Jeffery setupEndpoint(device);
154275f7c39SAndrew Jeffery }
155275f7c39SAndrew Jeffery catch (const std::system_error& e)
156275f7c39SAndrew Jeffery {
157275f7c39SAndrew Jeffery if (e.code() != std::errc::device_or_resource_busy)
158275f7c39SAndrew Jeffery {
159275f7c39SAndrew Jeffery throw e;
160275f7c39SAndrew Jeffery }
161275f7c39SAndrew Jeffery
162275f7c39SAndrew Jeffery auto current = devices.deviceFor(path);
163275f7c39SAndrew Jeffery if (!current)
164275f7c39SAndrew Jeffery {
165275f7c39SAndrew Jeffery warning(
166275f7c39SAndrew Jeffery "Invalid state: Failed to manage device for inventory at '{INVENTORY_PATH}', but the inventory item is unrecognised",
167275f7c39SAndrew Jeffery "INVENTORY_PATH", path);
168275f7c39SAndrew Jeffery return;
169275f7c39SAndrew Jeffery }
170275f7c39SAndrew Jeffery
171275f7c39SAndrew Jeffery // TODO: Ensure remove completion happens-before add. For now this
172275f7c39SAndrew Jeffery // happens unsynchronised. Make some noise about it.
173275f7c39SAndrew Jeffery warning(
174275f7c39SAndrew Jeffery "Unsynchronised endpoint reinitialsation due to configuration change at '{INVENTORY_PATH}': Removing '{MCTP_DEVICE}'",
175275f7c39SAndrew Jeffery "INVENTORY_PATH", path, "MCTP_DEVICE", current->describe());
176275f7c39SAndrew Jeffery
177275f7c39SAndrew Jeffery unmanageMCTPDevice(path);
178275f7c39SAndrew Jeffery
179275f7c39SAndrew Jeffery devices.add(path, device);
180275f7c39SAndrew Jeffery
181275f7c39SAndrew Jeffery // Pray (this is the unsynchronised bit)
182275f7c39SAndrew Jeffery deferSetup(device);
183275f7c39SAndrew Jeffery }
184275f7c39SAndrew Jeffery }
185275f7c39SAndrew Jeffery
unmanageMCTPDevice(const std::string & path)186275f7c39SAndrew Jeffery void MCTPReactor::unmanageMCTPDevice(const std::string& path)
187275f7c39SAndrew Jeffery {
188275f7c39SAndrew Jeffery auto device = devices.deviceFor(path);
189275f7c39SAndrew Jeffery if (!device)
190275f7c39SAndrew Jeffery {
191275f7c39SAndrew Jeffery debug("Unrecognised inventory item: {INVENTORY_PATH}", "INVENTORY_PATH",
192275f7c39SAndrew Jeffery path);
193275f7c39SAndrew Jeffery return;
194275f7c39SAndrew Jeffery }
195275f7c39SAndrew Jeffery
196275f7c39SAndrew Jeffery debug("MCTP device inventory removed at '{INVENTORY_PATH}'",
197275f7c39SAndrew Jeffery "INVENTORY_PATH", path);
198275f7c39SAndrew Jeffery
199275f7c39SAndrew Jeffery deferred.erase(device);
200275f7c39SAndrew Jeffery
201275f7c39SAndrew Jeffery // Remove the device from the repository before notifying the device itself
202275f7c39SAndrew Jeffery // of removal so we don't defer its setup
203275f7c39SAndrew Jeffery devices.remove(device);
204275f7c39SAndrew Jeffery
205275f7c39SAndrew Jeffery debug("Stopping management of MCTP device at [ {MCTP_DEVICE} ]",
206275f7c39SAndrew Jeffery "MCTP_DEVICE", device->describe());
207275f7c39SAndrew Jeffery
208275f7c39SAndrew Jeffery device->remove();
209275f7c39SAndrew Jeffery }
210