1f60ac27eSMatt Spinler /** 2f60ac27eSMatt Spinler * Copyright © 2019 IBM Corporation 3f60ac27eSMatt Spinler * 4f60ac27eSMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 5f60ac27eSMatt Spinler * you may not use this file except in compliance with the License. 6f60ac27eSMatt Spinler * You may obtain a copy of the License at 7f60ac27eSMatt Spinler * 8f60ac27eSMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 9f60ac27eSMatt Spinler * 10f60ac27eSMatt Spinler * Unless required by applicable law or agreed to in writing, software 11f60ac27eSMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 12f60ac27eSMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f60ac27eSMatt Spinler * See the License for the specific language governing permissions and 14f60ac27eSMatt Spinler * limitations under the License. 15f60ac27eSMatt Spinler */ 16f60ac27eSMatt Spinler #include "host_notifier.hpp" 17f60ac27eSMatt Spinler 18f60ac27eSMatt Spinler #include <phosphor-logging/log.hpp> 19f60ac27eSMatt Spinler 20f60ac27eSMatt Spinler namespace openpower::pels 21f60ac27eSMatt Spinler { 22f60ac27eSMatt Spinler 23f60ac27eSMatt Spinler const auto subscriptionName = "PELHostNotifier"; 24f77debb9SMatt Spinler const size_t maxRetryAttempts = 15; 25f60ac27eSMatt Spinler 26f60ac27eSMatt Spinler using namespace phosphor::logging; 27f60ac27eSMatt Spinler 28f60ac27eSMatt Spinler HostNotifier::HostNotifier(Repository& repo, DataInterfaceBase& dataIface, 29f60ac27eSMatt Spinler std::unique_ptr<HostInterface> hostIface) : 30f60ac27eSMatt Spinler _repo(repo), 31f869fcf8SMatt Spinler _dataIface(dataIface), _hostIface(std::move(hostIface)), 32f869fcf8SMatt Spinler _retryTimer(_hostIface->getEvent(), 33f869fcf8SMatt Spinler std::bind(std::mem_fn(&HostNotifier::retryTimerExpired), this)) 34f60ac27eSMatt Spinler { 35f60ac27eSMatt Spinler // Subscribe to be told about new PELs. 36f60ac27eSMatt Spinler _repo.subscribeToAdds(subscriptionName, 37f60ac27eSMatt Spinler std::bind(std::mem_fn(&HostNotifier::newLogCallback), 38f60ac27eSMatt Spinler this, std::placeholders::_1)); 39f60ac27eSMatt Spinler 40f60ac27eSMatt Spinler // Add any existing PELs to the queue to send them if necessary. 41f60ac27eSMatt Spinler _repo.for_each(std::bind(std::mem_fn(&HostNotifier::addPELToQueue), this, 42f60ac27eSMatt Spinler std::placeholders::_1)); 43f60ac27eSMatt Spinler 44f60ac27eSMatt Spinler // Subscribe to be told about host state changes. 45f60ac27eSMatt Spinler _dataIface.subscribeToHostStateChange( 46f60ac27eSMatt Spinler subscriptionName, 47f60ac27eSMatt Spinler std::bind(std::mem_fun(&HostNotifier::hostStateChange), this, 48f60ac27eSMatt Spinler std::placeholders::_1)); 49f60ac27eSMatt Spinler 50f60ac27eSMatt Spinler // Set the function to call when the async reponse is received. 51f60ac27eSMatt Spinler _hostIface->setResponseFunction( 52f60ac27eSMatt Spinler std::bind(std::mem_fn(&HostNotifier::commandResponse), this, 53f60ac27eSMatt Spinler std::placeholders::_1)); 54f60ac27eSMatt Spinler 55f60ac27eSMatt Spinler // Start sending logs if the host is running 56f60ac27eSMatt Spinler if (!_pelQueue.empty() && _dataIface.isHostUp()) 57f60ac27eSMatt Spinler { 58f60ac27eSMatt Spinler doNewLogNotify(); 59f60ac27eSMatt Spinler } 60f60ac27eSMatt Spinler } 61f60ac27eSMatt Spinler 62f60ac27eSMatt Spinler HostNotifier::~HostNotifier() 63f60ac27eSMatt Spinler { 64f60ac27eSMatt Spinler _repo.unsubscribeFromAdds(subscriptionName); 65f60ac27eSMatt Spinler _dataIface.unsubscribeFromHostStateChange(subscriptionName); 66f60ac27eSMatt Spinler } 67f60ac27eSMatt Spinler 68f60ac27eSMatt Spinler bool HostNotifier::addPELToQueue(const PEL& pel) 69f60ac27eSMatt Spinler { 70f60ac27eSMatt Spinler if (enqueueRequired(pel.id())) 71f60ac27eSMatt Spinler { 72f60ac27eSMatt Spinler _pelQueue.push_back(pel.id()); 73f60ac27eSMatt Spinler } 74f60ac27eSMatt Spinler 75f60ac27eSMatt Spinler // Return false so that Repo::for_each keeps going. 76f60ac27eSMatt Spinler return false; 77f60ac27eSMatt Spinler } 78f60ac27eSMatt Spinler 79f60ac27eSMatt Spinler bool HostNotifier::enqueueRequired(uint32_t id) const 80f60ac27eSMatt Spinler { 81f60ac27eSMatt Spinler bool required = true; 82a943b15bSMatt Spinler Repository::LogID i{Repository::LogID::Pel{id}}; 83a943b15bSMatt Spinler 84a943b15bSMatt Spinler if (auto attributes = _repo.getPELAttributes(i); attributes) 85a943b15bSMatt Spinler { 86a943b15bSMatt Spinler auto a = attributes.value().get(); 87a943b15bSMatt Spinler 88a943b15bSMatt Spinler if ((a.hostState == TransmissionState::acked) || 89a943b15bSMatt Spinler (a.hostState == TransmissionState::badPEL)) 90a943b15bSMatt Spinler { 91a943b15bSMatt Spinler required = false; 92a943b15bSMatt Spinler } 93a943b15bSMatt Spinler else if (a.actionFlags.test(hiddenFlagBit) && 94a943b15bSMatt Spinler (a.hmcState == TransmissionState::acked)) 95a943b15bSMatt Spinler { 96a943b15bSMatt Spinler required = false; 97a943b15bSMatt Spinler } 98a943b15bSMatt Spinler else if (a.actionFlags.test(dontReportToHostFlagBit)) 99a943b15bSMatt Spinler { 100a943b15bSMatt Spinler required = false; 101a943b15bSMatt Spinler } 102a943b15bSMatt Spinler } 103a943b15bSMatt Spinler else 104a943b15bSMatt Spinler { 105a943b15bSMatt Spinler using namespace phosphor::logging; 106a943b15bSMatt Spinler log<level::ERR>("Host Enqueue: Unable to find PEL ID in repository", 107a943b15bSMatt Spinler entry("PEL_ID=0x%X", id)); 108a943b15bSMatt Spinler required = false; 109a943b15bSMatt Spinler } 110f60ac27eSMatt Spinler 111f60ac27eSMatt Spinler return required; 112f60ac27eSMatt Spinler } 113f60ac27eSMatt Spinler 114f77debb9SMatt Spinler bool HostNotifier::notifyRequired(uint32_t id) const 115f77debb9SMatt Spinler { 116f77debb9SMatt Spinler bool notify = true; 117f77debb9SMatt Spinler Repository::LogID i{Repository::LogID::Pel{id}}; 118f77debb9SMatt Spinler 119f77debb9SMatt Spinler if (auto attributes = _repo.getPELAttributes(i); attributes) 120f77debb9SMatt Spinler { 121f77debb9SMatt Spinler // If already acked by the host, don't send again. 122f77debb9SMatt Spinler // (A safety check as it shouldn't get to this point.) 123f77debb9SMatt Spinler auto a = attributes.value().get(); 124f77debb9SMatt Spinler if (a.hostState == TransmissionState::acked) 125f77debb9SMatt Spinler { 126f77debb9SMatt Spinler notify = false; 127f77debb9SMatt Spinler } 128f77debb9SMatt Spinler else if (a.actionFlags.test(hiddenFlagBit)) 129f77debb9SMatt Spinler { 130f77debb9SMatt Spinler // If hidden and acked (or will be) acked by the HMC, 131f77debb9SMatt Spinler // also don't send it. (HMC management can come and 132f77debb9SMatt Spinler // go at any time) 133f77debb9SMatt Spinler if ((a.hmcState == TransmissionState::acked) || 134f77debb9SMatt Spinler _dataIface.isHMCManaged()) 135f77debb9SMatt Spinler { 136f77debb9SMatt Spinler notify = false; 137f77debb9SMatt Spinler } 138f77debb9SMatt Spinler } 139f77debb9SMatt Spinler } 140f77debb9SMatt Spinler else 141f77debb9SMatt Spinler { 142f77debb9SMatt Spinler // Must have been deleted since put on the queue. 143f77debb9SMatt Spinler notify = false; 144f77debb9SMatt Spinler } 145f77debb9SMatt Spinler 146f77debb9SMatt Spinler return notify; 147f77debb9SMatt Spinler } 148f77debb9SMatt Spinler 149f60ac27eSMatt Spinler void HostNotifier::newLogCallback(const PEL& pel) 150f60ac27eSMatt Spinler { 151f60ac27eSMatt Spinler if (!enqueueRequired(pel.id())) 152f60ac27eSMatt Spinler { 153f60ac27eSMatt Spinler return; 154f60ac27eSMatt Spinler } 155f60ac27eSMatt Spinler 156f60ac27eSMatt Spinler _pelQueue.push_back(pel.id()); 157f60ac27eSMatt Spinler 1587d800a4eSMatt Spinler if (!_dataIface.isHostUp()) 1597d800a4eSMatt Spinler { 1607d800a4eSMatt Spinler return; 1617d800a4eSMatt Spinler } 1627d800a4eSMatt Spinler 1637d800a4eSMatt Spinler // Dispatch a command now if there isn't currently a command 1647d800a4eSMatt Spinler // in progress and this is the first log in the queue or it 1657d800a4eSMatt Spinler // previously gave up from a hard failure. 1667d800a4eSMatt Spinler auto inProgress = (_inProgressPEL != 0) || _hostIface->cmdInProgress() || 1677d800a4eSMatt Spinler _retryTimer.isEnabled(); 1687d800a4eSMatt Spinler 1697d800a4eSMatt Spinler auto firstPEL = _pelQueue.size() == 1; 1707d800a4eSMatt Spinler auto gaveUp = _retryCount >= maxRetryAttempts; 1717d800a4eSMatt Spinler 1727d800a4eSMatt Spinler if (!inProgress && (firstPEL || gaveUp)) 1737d800a4eSMatt Spinler { 1747d800a4eSMatt Spinler _retryCount = 0; 1757d800a4eSMatt Spinler 1767d800a4eSMatt Spinler // Send a log, but from the event loop, not from here. 1777d800a4eSMatt Spinler scheduleDispatch(); 1787d800a4eSMatt Spinler } 1797d800a4eSMatt Spinler } 1807d800a4eSMatt Spinler 1817d800a4eSMatt Spinler void HostNotifier::scheduleDispatch() 1827d800a4eSMatt Spinler { 1837d800a4eSMatt Spinler _dispatcher = std::make_unique<sdeventplus::source::Defer>( 1847d800a4eSMatt Spinler _hostIface->getEvent(), std::bind(std::mem_fn(&HostNotifier::dispatch), 1857d800a4eSMatt Spinler this, std::placeholders::_1)); 1867d800a4eSMatt Spinler } 1877d800a4eSMatt Spinler 1887d800a4eSMatt Spinler void HostNotifier::dispatch(sdeventplus::source::EventBase& source) 1897d800a4eSMatt Spinler { 1907d800a4eSMatt Spinler _dispatcher.reset(); 1917d800a4eSMatt Spinler 1927d800a4eSMatt Spinler doNewLogNotify(); 193f60ac27eSMatt Spinler } 194f60ac27eSMatt Spinler 195f60ac27eSMatt Spinler void HostNotifier::doNewLogNotify() 196f60ac27eSMatt Spinler { 197f77debb9SMatt Spinler if (!_dataIface.isHostUp() || _retryTimer.isEnabled()) 198f77debb9SMatt Spinler { 199f77debb9SMatt Spinler return; 200f77debb9SMatt Spinler } 201f77debb9SMatt Spinler 202f77debb9SMatt Spinler if (_retryCount >= maxRetryAttempts) 203f77debb9SMatt Spinler { 204f77debb9SMatt Spinler // Give up until a new log comes in. 205f77debb9SMatt Spinler if (_retryCount == maxRetryAttempts) 206f77debb9SMatt Spinler { 207f77debb9SMatt Spinler // If this were to really happen, the PLDM interface 208f77debb9SMatt Spinler // would be down and isolating that shouldn't left to 209f77debb9SMatt Spinler // a logging daemon, so just trace. Also, this will start 210f77debb9SMatt Spinler // trying again when the next new log comes in. 211f77debb9SMatt Spinler log<level::ERR>( 212f77debb9SMatt Spinler "PEL Host notifier hit max retry attempts. Giving up for now.", 213f77debb9SMatt Spinler entry("PEL_ID=0x%X", _pelQueue.front())); 214f77debb9SMatt Spinler } 215f77debb9SMatt Spinler return; 216f77debb9SMatt Spinler } 217f77debb9SMatt Spinler 218f77debb9SMatt Spinler bool doNotify = false; 219f77debb9SMatt Spinler uint32_t id = 0; 220f77debb9SMatt Spinler 221f77debb9SMatt Spinler // Find the PEL to send 222f77debb9SMatt Spinler while (!doNotify && !_pelQueue.empty()) 223f77debb9SMatt Spinler { 224f77debb9SMatt Spinler id = _pelQueue.front(); 225f77debb9SMatt Spinler _pelQueue.pop_front(); 226f77debb9SMatt Spinler 227f77debb9SMatt Spinler if (notifyRequired(id)) 228f77debb9SMatt Spinler { 229f77debb9SMatt Spinler doNotify = true; 230f77debb9SMatt Spinler } 231f77debb9SMatt Spinler } 232f77debb9SMatt Spinler 233f77debb9SMatt Spinler if (doNotify) 234f77debb9SMatt Spinler { 235f77debb9SMatt Spinler // Get the size using the repo attributes 236f77debb9SMatt Spinler Repository::LogID i{Repository::LogID::Pel{id}}; 237f77debb9SMatt Spinler if (auto attributes = _repo.getPELAttributes(i); attributes) 238f77debb9SMatt Spinler { 239f77debb9SMatt Spinler auto size = static_cast<size_t>( 240f77debb9SMatt Spinler std::filesystem::file_size((*attributes).get().path)); 241f77debb9SMatt Spinler auto rc = _hostIface->sendNewLogCmd(id, size); 242f77debb9SMatt Spinler 243f77debb9SMatt Spinler if (rc == CmdStatus::success) 244f77debb9SMatt Spinler { 245f77debb9SMatt Spinler _inProgressPEL = id; 246f77debb9SMatt Spinler } 247f77debb9SMatt Spinler else 248f77debb9SMatt Spinler { 249f77debb9SMatt Spinler // It failed. Retry 250f77debb9SMatt Spinler log<level::ERR>("PLDM send failed", entry("PEL_ID=0x%X", id)); 251f77debb9SMatt Spinler _pelQueue.push_front(id); 252f77debb9SMatt Spinler _inProgressPEL = 0; 253f77debb9SMatt Spinler _retryTimer.restartOnce(_hostIface->getSendRetryDelay()); 254f77debb9SMatt Spinler } 255f77debb9SMatt Spinler } 256f77debb9SMatt Spinler else 257f77debb9SMatt Spinler { 258f77debb9SMatt Spinler log<level::ERR>("PEL ID not in repository. Cannot notify host", 259f77debb9SMatt Spinler entry("PEL_ID=0x%X", id)); 260f77debb9SMatt Spinler } 261f77debb9SMatt Spinler } 262f60ac27eSMatt Spinler } 263f60ac27eSMatt Spinler 264f60ac27eSMatt Spinler void HostNotifier::hostStateChange(bool hostUp) 265f60ac27eSMatt Spinler { 2663019c6fbSMatt Spinler _retryCount = 0; 2673019c6fbSMatt Spinler 2683019c6fbSMatt Spinler if (hostUp && !_pelQueue.empty()) 2693019c6fbSMatt Spinler { 2703019c6fbSMatt Spinler doNewLogNotify(); 2713019c6fbSMatt Spinler } 2723019c6fbSMatt Spinler else if (!hostUp) 2733019c6fbSMatt Spinler { 2743019c6fbSMatt Spinler stopCommand(); 2753019c6fbSMatt Spinler 2763019c6fbSMatt Spinler // Reset the state on any PELs that were sent but not acked back 2773019c6fbSMatt Spinler // to new so they'll get sent again. 2783019c6fbSMatt Spinler for (auto id : _sentPELs) 2793019c6fbSMatt Spinler { 2803019c6fbSMatt Spinler _pelQueue.push_back(id); 2813019c6fbSMatt Spinler _repo.setPELHostTransState(id, TransmissionState::newPEL); 2823019c6fbSMatt Spinler } 2833019c6fbSMatt Spinler 2843019c6fbSMatt Spinler _sentPELs.clear(); 2853019c6fbSMatt Spinler } 286f60ac27eSMatt Spinler } 287f60ac27eSMatt Spinler 288f60ac27eSMatt Spinler void HostNotifier::commandResponse(ResponseStatus status) 289f60ac27eSMatt Spinler { 290f869fcf8SMatt Spinler auto id = _inProgressPEL; 291f869fcf8SMatt Spinler _inProgressPEL = 0; 292f869fcf8SMatt Spinler 293f869fcf8SMatt Spinler if (status == ResponseStatus::success) 294f869fcf8SMatt Spinler { 295f869fcf8SMatt Spinler _retryCount = 0; 296f869fcf8SMatt Spinler 297f869fcf8SMatt Spinler _sentPELs.push_back(id); 298f869fcf8SMatt Spinler 299f869fcf8SMatt Spinler _repo.setPELHostTransState(id, TransmissionState::sent); 300f869fcf8SMatt Spinler 301f869fcf8SMatt Spinler if (!_pelQueue.empty()) 302f869fcf8SMatt Spinler { 303f869fcf8SMatt Spinler doNewLogNotify(); 304f869fcf8SMatt Spinler } 305f869fcf8SMatt Spinler } 306f869fcf8SMatt Spinler else 307f869fcf8SMatt Spinler { 308f869fcf8SMatt Spinler log<level::ERR>("PLDM command response failure", 309f869fcf8SMatt Spinler entry("PEL_ID=0x%X", id)); 310f869fcf8SMatt Spinler // Retry 311f869fcf8SMatt Spinler _pelQueue.push_front(id); 312f869fcf8SMatt Spinler _retryTimer.restartOnce(_hostIface->getReceiveRetryDelay()); 313f869fcf8SMatt Spinler } 314f869fcf8SMatt Spinler } 315f869fcf8SMatt Spinler 316f869fcf8SMatt Spinler void HostNotifier::retryTimerExpired() 317f869fcf8SMatt Spinler { 318f869fcf8SMatt Spinler if (_dataIface.isHostUp()) 319f869fcf8SMatt Spinler { 320f869fcf8SMatt Spinler log<level::INFO>("Attempting command retry", 321f869fcf8SMatt Spinler entry("PEL_ID=0x%X", _pelQueue.front())); 322f869fcf8SMatt Spinler _retryCount++; 323f869fcf8SMatt Spinler doNewLogNotify(); 324f869fcf8SMatt Spinler } 325f60ac27eSMatt Spinler } 326f60ac27eSMatt Spinler 3273019c6fbSMatt Spinler void HostNotifier::stopCommand() 3283019c6fbSMatt Spinler { 3293019c6fbSMatt Spinler _retryCount = 0; 3303019c6fbSMatt Spinler 3313019c6fbSMatt Spinler if (_inProgressPEL != 0) 3323019c6fbSMatt Spinler { 3333019c6fbSMatt Spinler _pelQueue.push_front(_inProgressPEL); 3343019c6fbSMatt Spinler _inProgressPEL = 0; 3353019c6fbSMatt Spinler } 3363019c6fbSMatt Spinler 3373019c6fbSMatt Spinler if (_retryTimer.isEnabled()) 3383019c6fbSMatt Spinler { 3393019c6fbSMatt Spinler _retryTimer.setEnabled(false); 3403019c6fbSMatt Spinler } 3413019c6fbSMatt Spinler 3423019c6fbSMatt Spinler if (_hostIface->cmdInProgress()) 3433019c6fbSMatt Spinler { 3443019c6fbSMatt Spinler _hostIface->cancelCmd(); 3453019c6fbSMatt Spinler } 3463019c6fbSMatt Spinler } 3473019c6fbSMatt Spinler 348*cc3b64aeSMatt Spinler void HostNotifier::ackPEL(uint32_t id) 349*cc3b64aeSMatt Spinler { 350*cc3b64aeSMatt Spinler _repo.setPELHostTransState(id, TransmissionState::acked); 351*cc3b64aeSMatt Spinler 352*cc3b64aeSMatt Spinler // No longer just 'sent', so remove it from the sent list. 353*cc3b64aeSMatt Spinler auto sent = std::find(_sentPELs.begin(), _sentPELs.end(), id); 354*cc3b64aeSMatt Spinler if (sent != _sentPELs.end()) 355*cc3b64aeSMatt Spinler { 356*cc3b64aeSMatt Spinler _sentPELs.erase(sent); 357*cc3b64aeSMatt Spinler } 358*cc3b64aeSMatt Spinler } 359*cc3b64aeSMatt Spinler 360f60ac27eSMatt Spinler } // namespace openpower::pels 361