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 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 27 void MCTPReactor::untrackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep) 28 { 29 server.disassociate(MCTPDEndpoint::path(ep)); 30 } 31 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 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 130 void MCTPReactor::tick() 131 { 132 auto toSetup = std::exchange(deferred, {}); 133 for (const auto& entry : toSetup) 134 { 135 setupEndpoint(entry); 136 } 137 } 138 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 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