xref: /openbmc/dbus-sensors/src/mctp/MCTPReactor.cpp (revision b10853e29d6b411b47563a5d97827ad9c8f10692)
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