xref: /openbmc/dbus-sensors/src/mctp/MCTPReactor.cpp (revision 19d1fda609a4c39caca73858c90ebee823028362)
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 /au/com/codeconstruct/mctp1/networks/1/endpoints/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