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