xref: /openbmc/dbus-sensors/src/mctp/MCTPReactor.cpp (revision 0d2f96f1e95a6a37f00a3c458c8562d6af92e4a4)
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 for '{MCTP_ENDPOINT}'",
103                 "MCTP_ENDPOINT", ep->describe());
104             return;
105         }
106 
107         if (ec)
108         {
109             debug(
110                 "Setup failed for MCTP device at [ {MCTP_DEVICE} ]: {ERROR_MESSAGE}",
111                 "MCTP_DEVICE", dev->describe(), "ERROR_MESSAGE", ec.message());
112 
113             self->deferSetup(dev);
114             return;
115         }
116 
117         try
118         {
119             self->trackEndpoint(ep);
120         }
121         catch (const MCTPException& e)
122         {
123             error("Failed to track endpoint '{MCTP_ENDPOINT}': {EXCEPTION}",
124                   "MCTP_ENDPOINT", ep->describe(), "EXCEPTION", e);
125             self->deferSetup(dev);
126         }
127     });
128 }
129 
tick()130 void MCTPReactor::tick()
131 {
132     auto toSetup = std::exchange(deferred, {});
133     for (const auto& entry : toSetup)
134     {
135         setupEndpoint(entry);
136     }
137 }
138 
manageMCTPDevice(const std::string & path,const std::shared_ptr<MCTPDevice> & device)139 void MCTPReactor::manageMCTPDevice(const std::string& path,
140                                    const std::shared_ptr<MCTPDevice>& device)
141 {
142     if (!device)
143     {
144         return;
145     }
146 
147     try
148     {
149         devices.add(path, device);
150         debug("MCTP device inventory added at '{INVENTORY_PATH}'",
151               "INVENTORY_PATH", path);
152         setupEndpoint(device);
153     }
154     catch (const std::system_error& e)
155     {
156         if (e.code() != std::errc::device_or_resource_busy)
157         {
158             throw e;
159         }
160 
161         auto current = devices.deviceFor(path);
162         if (!current)
163         {
164             warning(
165                 "Invalid state: Failed to manage device for inventory at '{INVENTORY_PATH}', but the inventory item is unrecognised",
166                 "INVENTORY_PATH", path);
167             return;
168         }
169 
170         // TODO: Ensure remove completion happens-before add. For now this
171         // happens unsynchronised. Make some noise about it.
172         warning(
173             "Unsynchronised endpoint reinitialsation due to configuration change at '{INVENTORY_PATH}': Removing '{MCTP_DEVICE}'",
174             "INVENTORY_PATH", path, "MCTP_DEVICE", current->describe());
175 
176         unmanageMCTPDevice(path);
177 
178         devices.add(path, device);
179 
180         // Pray (this is the unsynchronised bit)
181         deferSetup(device);
182     }
183 }
184 
unmanageMCTPDevice(const std::string & path)185 void MCTPReactor::unmanageMCTPDevice(const std::string& path)
186 {
187     auto device = devices.deviceFor(path);
188     if (!device)
189     {
190         debug("Unrecognised inventory item: {INVENTORY_PATH}", "INVENTORY_PATH",
191               path);
192         return;
193     }
194 
195     debug("MCTP device inventory removed at '{INVENTORY_PATH}'",
196           "INVENTORY_PATH", path);
197 
198     deferred.erase(device);
199 
200     // Remove the device from the repository before notifying the device itself
201     // of removal so we don't defer its setup
202     devices.remove(device);
203 
204     debug("Stopping management of MCTP device at [ {MCTP_DEVICE} ]",
205           "MCTP_DEVICE", device->describe());
206 
207     device->remove();
208 }
209