1 #include "MCTPReactor.hpp"
2
3 #include "MCTPDeviceRepository.hpp"
4 #include "MCTPEndpoint.hpp"
5 #include "Utils.hpp"
6
7 #include <boost/system/detail/error_code.hpp>
8 #include <phosphor-logging/lg2.hpp>
9
10 #include <memory>
11 #include <optional>
12 #include <string>
13 #include <system_error>
14 #include <utility>
15 #include <vector>
16
17 PHOSPHOR_LOG2_USING;
18
deferSetup(const std::shared_ptr<MCTPDevice> & dev)19 void MCTPReactor::deferSetup(const std::shared_ptr<MCTPDevice>& dev)
20 {
21 debug("Deferring setup for MCTP device at [ {MCTP_DEVICE} ]", "MCTP_DEVICE",
22 dev->describe());
23
24 deferred.emplace(dev);
25 }
26
untrackEndpoint(const std::shared_ptr<MCTPEndpoint> & ep)27 void MCTPReactor::untrackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep)
28 {
29 server.disassociate(MCTPDEndpoint::path(ep));
30 }
31
trackEndpoint(const std::shared_ptr<MCTPEndpoint> & ep)32 void MCTPReactor::trackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep)
33 {
34 info("Added MCTP endpoint to device: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
35 ep->describe());
36
37 ep->subscribe(
38 // Degraded
39 [](const std::shared_ptr<MCTPEndpoint>& ep) {
40 debug("Endpoint entered degraded state: [ {MCTP_ENDPOINT} ]",
41 "MCTP_ENDPOINT", ep->describe());
42 },
43 // Available
44 [](const std::shared_ptr<MCTPEndpoint>& ep) {
45 debug("Endpoint entered available state: [ {MCTP_ENDPOINT} ]",
46 "MCTP_ENDPOINT", ep->describe());
47 },
48 // Removed
49 [weak{weak_from_this()}](const std::shared_ptr<MCTPEndpoint>& ep) {
50 info("Removed MCTP endpoint from device: [ {MCTP_ENDPOINT} ]",
51 "MCTP_ENDPOINT", ep->describe());
52 if (auto self = weak.lock())
53 {
54 self->untrackEndpoint(ep);
55 // Only defer the setup if we know inventory is still present
56 if (self->devices.contains(ep->device()))
57 {
58 self->deferSetup(ep->device());
59 }
60 }
61 else
62 {
63 info(
64 "The reactor object was destroyed concurrent to the removal of the remove match for the endpoint '{MCTP_ENDPOINT}'",
65 "MCTP_ENDPOINT", ep->describe());
66 }
67 });
68
69 // Proxy-host the association back to the inventory at the same path as the
70 // endpoint in mctpd.
71 //
72 // clang-format off
73 // ```
74 // # 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
75 // 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"
76 // ```
77 // clang-format on
78 std::optional<std::string> item = devices.inventoryFor(ep->device());
79 if (!item)
80 {
81 error("Inventory missing for endpoint: [ {MCTP_ENDPOINT} ]",
82 "MCTP_ENDPOINT", ep->describe());
83 return;
84 }
85 std::vector<Association> associations{
86 {"configured_by", "configures", *item}};
87 server.associate(MCTPDEndpoint::path(ep), associations);
88 }
89
setupEndpoint(const std::shared_ptr<MCTPDevice> & dev)90 void MCTPReactor::setupEndpoint(const std::shared_ptr<MCTPDevice>& dev)
91 {
92 debug(
93 "Attempting to setup up MCTP endpoint for device at [ {MCTP_DEVICE} ]",
94 "MCTP_DEVICE", dev->describe());
95 dev->setup([weak{weak_from_this()},
96 dev](const std::error_code& ec,
97 const std::shared_ptr<MCTPEndpoint>& ep) mutable {
98 auto self = weak.lock();
99 if (!self)
100 {
101 info(
102 "The reactor object was destroyed concurrent to the completion of the endpoint setup");
103 return;
104 }
105
106 if (ec)
107 {
108 debug(
109 "Setup failed for MCTP device at [ {MCTP_DEVICE} ]: {ERROR_MESSAGE}",
110 "MCTP_DEVICE", dev->describe(), "ERROR_MESSAGE", ec.message());
111
112 self->deferSetup(dev);
113 return;
114 }
115
116 try
117 {
118 self->trackEndpoint(ep);
119 }
120 catch (const MCTPException& e)
121 {
122 error("Failed to track endpoint '{MCTP_ENDPOINT}': {EXCEPTION}",
123 "MCTP_ENDPOINT", ep->describe(), "EXCEPTION", e);
124 self->deferSetup(dev);
125 }
126 });
127 }
128
tick()129 void MCTPReactor::tick()
130 {
131 auto toSetup = std::exchange(deferred, {});
132 for (const auto& entry : toSetup)
133 {
134 setupEndpoint(entry);
135 }
136 }
137
manageMCTPDevice(const std::string & path,const std::shared_ptr<MCTPDevice> & device)138 void MCTPReactor::manageMCTPDevice(const std::string& path,
139 const std::shared_ptr<MCTPDevice>& device)
140 {
141 if (!device)
142 {
143 return;
144 }
145
146 try
147 {
148 devices.add(path, device);
149 debug("MCTP device inventory added at '{INVENTORY_PATH}'",
150 "INVENTORY_PATH", path);
151 setupEndpoint(device);
152 }
153 catch (const std::system_error& e)
154 {
155 if (e.code() != std::errc::device_or_resource_busy)
156 {
157 throw e;
158 }
159
160 auto current = devices.deviceFor(path);
161 if (!current)
162 {
163 warning(
164 "Invalid state: Failed to manage device for inventory at '{INVENTORY_PATH}', but the inventory item is unrecognised",
165 "INVENTORY_PATH", path);
166 return;
167 }
168
169 // TODO: Ensure remove completion happens-before add. For now this
170 // happens unsynchronised. Make some noise about it.
171 warning(
172 "Unsynchronised endpoint reinitialsation due to configuration change at '{INVENTORY_PATH}': Removing '{MCTP_DEVICE}'",
173 "INVENTORY_PATH", path, "MCTP_DEVICE", current->describe());
174
175 unmanageMCTPDevice(path);
176
177 devices.add(path, device);
178
179 // Pray (this is the unsynchronised bit)
180 deferSetup(device);
181 }
182 }
183
unmanageMCTPDevice(const std::string & path)184 void MCTPReactor::unmanageMCTPDevice(const std::string& path)
185 {
186 auto device = devices.deviceFor(path);
187 if (!device)
188 {
189 debug("Unrecognised inventory item: {INVENTORY_PATH}", "INVENTORY_PATH",
190 path);
191 return;
192 }
193
194 debug("MCTP device inventory removed at '{INVENTORY_PATH}'",
195 "INVENTORY_PATH", path);
196
197 deferred.erase(device);
198
199 // Remove the device from the repository before notifying the device itself
200 // of removal so we don't defer its setup
201 devices.remove(device);
202
203 debug("Stopping management of MCTP device at [ {MCTP_DEVICE} ]",
204 "MCTP_DEVICE", device->describe());
205
206 device->remove();
207 }
208