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