blob: fee1d56f318a586b2c7898f1314894a80a347fe9 [file] [log] [blame]
Matt Spinlerf60ac272019-12-11 13:47:50 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "host_notifier.hpp"
17
Matt Spinler1b418862023-06-29 12:37:41 -050018#include <phosphor-logging/lg2.hpp>
Matt Spinlerf60ac272019-12-11 13:47:50 -060019
20namespace openpower::pels
21{
22
23const auto subscriptionName = "PELHostNotifier";
Matt Spinlerf77debb2019-12-12 10:04:33 -060024const size_t maxRetryAttempts = 15;
Matt Spinlerf60ac272019-12-11 13:47:50 -060025
Matt Spinlerf60ac272019-12-11 13:47:50 -060026HostNotifier::HostNotifier(Repository& repo, DataInterfaceBase& dataIface,
27 std::unique_ptr<HostInterface> hostIface) :
Patrick Williams075c7922024-08-16 15:19:49 -040028 _repo(repo), _dataIface(dataIface), _hostIface(std::move(hostIface)),
Matt Spinlerf869fcf2019-12-11 15:02:20 -060029 _retryTimer(_hostIface->getEvent(),
Matt Spinler41293cb2019-12-12 13:11:09 -060030 std::bind(std::mem_fn(&HostNotifier::retryTimerExpired), this)),
31 _hostFullTimer(
32 _hostIface->getEvent(),
Matt Spinlere5f75082022-01-24 16:09:51 -060033 std::bind(std::mem_fn(&HostNotifier::hostFullTimerExpired), this)),
Patrick Williams075c7922024-08-16 15:19:49 -040034 _hostUpTimer(_hostIface->getEvent(),
35 std::bind(std::mem_fn(&HostNotifier::hostUpTimerExpired),
36 this))
Matt Spinlerf60ac272019-12-11 13:47:50 -060037{
38 // Subscribe to be told about new PELs.
39 _repo.subscribeToAdds(subscriptionName,
40 std::bind(std::mem_fn(&HostNotifier::newLogCallback),
41 this, std::placeholders::_1));
42
Matt Spinler7cb985f2020-03-05 16:02:39 -060043 // Subscribe to be told about deleted PELs.
44 _repo.subscribeToDeletes(
45 subscriptionName,
46 std::bind(std::mem_fn(&HostNotifier::deleteLogCallback), this,
47 std::placeholders::_1));
48
Matt Spinlerf60ac272019-12-11 13:47:50 -060049 // Add any existing PELs to the queue to send them if necessary.
50 _repo.for_each(std::bind(std::mem_fn(&HostNotifier::addPELToQueue), this,
51 std::placeholders::_1));
52
53 // Subscribe to be told about host state changes.
54 _dataIface.subscribeToHostStateChange(
Matt Spinler4f1bed72022-06-09 09:06:15 -050055 subscriptionName, std::bind(std::mem_fn(&HostNotifier::hostStateChange),
56 this, std::placeholders::_1));
Matt Spinlerf60ac272019-12-11 13:47:50 -060057
58 // Set the function to call when the async reponse is received.
59 _hostIface->setResponseFunction(
60 std::bind(std::mem_fn(&HostNotifier::commandResponse), this,
61 std::placeholders::_1));
62
63 // Start sending logs if the host is running
64 if (!_pelQueue.empty() && _dataIface.isHostUp())
65 {
Matt Spinler1b418862023-06-29 12:37:41 -050066 lg2::debug("Host is already up at startup");
Matt Spinlere5f75082022-01-24 16:09:51 -060067 _hostUpTimer.restartOnce(_hostIface->getHostUpDelay());
Matt Spinlerf60ac272019-12-11 13:47:50 -060068 }
69}
70
71HostNotifier::~HostNotifier()
72{
73 _repo.unsubscribeFromAdds(subscriptionName);
74 _dataIface.unsubscribeFromHostStateChange(subscriptionName);
75}
76
Matt Spinlere5f75082022-01-24 16:09:51 -060077void HostNotifier::hostUpTimerExpired()
78{
Matt Spinler1b418862023-06-29 12:37:41 -050079 lg2::debug("Host up timer expired");
Matt Spinlere5f75082022-01-24 16:09:51 -060080 doNewLogNotify();
81}
82
Matt Spinlerf60ac272019-12-11 13:47:50 -060083bool HostNotifier::addPELToQueue(const PEL& pel)
84{
85 if (enqueueRequired(pel.id()))
86 {
87 _pelQueue.push_back(pel.id());
88 }
89
90 // Return false so that Repo::for_each keeps going.
91 return false;
92}
93
94bool HostNotifier::enqueueRequired(uint32_t id) const
95{
96 bool required = true;
Matt Spinlera943b152019-12-11 14:44:50 -060097 Repository::LogID i{Repository::LogID::Pel{id}};
98
Matt Spinler24a85582020-01-27 16:40:21 -060099 // Manufacturing testing may turn off sending up PELs
100 if (!_dataIface.getHostPELEnablement())
101 {
102 return false;
103 }
104
Matt Spinlera943b152019-12-11 14:44:50 -0600105 if (auto attributes = _repo.getPELAttributes(i); attributes)
106 {
107 auto a = attributes.value().get();
108
109 if ((a.hostState == TransmissionState::acked) ||
110 (a.hostState == TransmissionState::badPEL))
111 {
112 required = false;
113 }
114 else if (a.actionFlags.test(hiddenFlagBit) &&
115 (a.hmcState == TransmissionState::acked))
116 {
117 required = false;
118 }
119 else if (a.actionFlags.test(dontReportToHostFlagBit))
120 {
121 required = false;
122 }
123 }
124 else
125 {
126 using namespace phosphor::logging;
Matt Spinler1b418862023-06-29 12:37:41 -0500127 lg2::error("Host Enqueue: Unable to find PEL ID {ID} in repository",
128 "ID", lg2::hex, id);
Matt Spinlera943b152019-12-11 14:44:50 -0600129 required = false;
130 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600131
132 return required;
133}
134
Matt Spinlerf77debb2019-12-12 10:04:33 -0600135bool HostNotifier::notifyRequired(uint32_t id) const
136{
137 bool notify = true;
138 Repository::LogID i{Repository::LogID::Pel{id}};
139
140 if (auto attributes = _repo.getPELAttributes(i); attributes)
141 {
142 // If already acked by the host, don't send again.
143 // (A safety check as it shouldn't get to this point.)
144 auto a = attributes.value().get();
145 if (a.hostState == TransmissionState::acked)
146 {
147 notify = false;
148 }
149 else if (a.actionFlags.test(hiddenFlagBit))
150 {
151 // If hidden and acked (or will be) acked by the HMC,
152 // also don't send it. (HMC management can come and
153 // go at any time)
154 if ((a.hmcState == TransmissionState::acked) ||
155 _dataIface.isHMCManaged())
156 {
157 notify = false;
158 }
159 }
160 }
161 else
162 {
163 // Must have been deleted since put on the queue.
164 notify = false;
165 }
166
167 return notify;
168}
169
Matt Spinlerf60ac272019-12-11 13:47:50 -0600170void HostNotifier::newLogCallback(const PEL& pel)
171{
172 if (!enqueueRequired(pel.id()))
173 {
174 return;
175 }
176
Matt Spinler1b418862023-06-29 12:37:41 -0500177 lg2::debug("New PEL added to queue, PEL ID = {ID}", "ID", lg2::hex,
178 pel.id());
Matt Spinler5f5352e2020-03-05 16:23:27 -0600179
Matt Spinlerf60ac272019-12-11 13:47:50 -0600180 _pelQueue.push_back(pel.id());
181
Matt Spinlere5f75082022-01-24 16:09:51 -0600182 // Notify shouldn't happen if host is down, not up long enough, or full
183 if (!_dataIface.isHostUp() || _hostFull || _hostUpTimer.isEnabled())
Matt Spinler7d800a42019-12-12 10:35:01 -0600184 {
185 return;
186 }
187
188 // Dispatch a command now if there isn't currently a command
189 // in progress and this is the first log in the queue or it
190 // previously gave up from a hard failure.
191 auto inProgress = (_inProgressPEL != 0) || _hostIface->cmdInProgress() ||
192 _retryTimer.isEnabled();
193
194 auto firstPEL = _pelQueue.size() == 1;
195 auto gaveUp = _retryCount >= maxRetryAttempts;
196
197 if (!inProgress && (firstPEL || gaveUp))
198 {
199 _retryCount = 0;
200
201 // Send a log, but from the event loop, not from here.
202 scheduleDispatch();
203 }
204}
205
Matt Spinler7cb985f2020-03-05 16:02:39 -0600206void HostNotifier::deleteLogCallback(uint32_t id)
207{
208 auto queueIt = std::find(_pelQueue.begin(), _pelQueue.end(), id);
209 if (queueIt != _pelQueue.end())
210 {
Matt Spinler1b418862023-06-29 12:37:41 -0500211 lg2::debug("Host notifier removing deleted log from queue");
Matt Spinler7cb985f2020-03-05 16:02:39 -0600212 _pelQueue.erase(queueIt);
213 }
214
215 auto sentIt = std::find(_sentPELs.begin(), _sentPELs.end(), id);
216 if (sentIt != _sentPELs.end())
217 {
Matt Spinler1b418862023-06-29 12:37:41 -0500218 lg2::debug("Host notifier removing deleted log from sent list");
Matt Spinler7cb985f2020-03-05 16:02:39 -0600219 _sentPELs.erase(sentIt);
220 }
221
222 // Nothing we can do about this...
223 if (id == _inProgressPEL)
224 {
Matt Spinler1b418862023-06-29 12:37:41 -0500225 lg2::warning(
226 "A PEL was deleted while its host notification was in progress, PEL ID = {ID}",
227 "ID", lg2::hex, id);
Matt Spinler7cb985f2020-03-05 16:02:39 -0600228 }
229}
230
Matt Spinler7d800a42019-12-12 10:35:01 -0600231void HostNotifier::scheduleDispatch()
232{
233 _dispatcher = std::make_unique<sdeventplus::source::Defer>(
234 _hostIface->getEvent(), std::bind(std::mem_fn(&HostNotifier::dispatch),
235 this, std::placeholders::_1));
236}
237
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500238void HostNotifier::dispatch(sdeventplus::source::EventBase& /*source*/)
Matt Spinler7d800a42019-12-12 10:35:01 -0600239{
240 _dispatcher.reset();
241
242 doNewLogNotify();
Matt Spinlerf60ac272019-12-11 13:47:50 -0600243}
244
245void HostNotifier::doNewLogNotify()
246{
Matt Spinler41293cb2019-12-12 13:11:09 -0600247 if (!_dataIface.isHostUp() || _retryTimer.isEnabled() ||
248 _hostFullTimer.isEnabled())
Matt Spinlerf77debb2019-12-12 10:04:33 -0600249 {
250 return;
251 }
252
253 if (_retryCount >= maxRetryAttempts)
254 {
255 // Give up until a new log comes in.
256 if (_retryCount == maxRetryAttempts)
257 {
258 // If this were to really happen, the PLDM interface
259 // would be down and isolating that shouldn't left to
260 // a logging daemon, so just trace. Also, this will start
261 // trying again when the next new log comes in.
Matt Spinler1b418862023-06-29 12:37:41 -0500262 lg2::error(
263 "PEL Host notifier hit max retry attempts. Giving up for now. PEL ID = {ID}",
264 "ID", lg2::hex, _pelQueue.front());
Matt Spinler829b0522020-03-04 08:38:46 -0600265
266 // Tell the host interface object to clean itself up, especially to
267 // release the PLDM instance ID it's been using.
268 _hostIface->cancelCmd();
Matt Spinlerf77debb2019-12-12 10:04:33 -0600269 }
270 return;
271 }
272
273 bool doNotify = false;
274 uint32_t id = 0;
275
276 // Find the PEL to send
277 while (!doNotify && !_pelQueue.empty())
278 {
279 id = _pelQueue.front();
280 _pelQueue.pop_front();
281
282 if (notifyRequired(id))
283 {
284 doNotify = true;
285 }
286 }
287
288 if (doNotify)
289 {
290 // Get the size using the repo attributes
291 Repository::LogID i{Repository::LogID::Pel{id}};
292 if (auto attributes = _repo.getPELAttributes(i); attributes)
293 {
294 auto size = static_cast<size_t>(
295 std::filesystem::file_size((*attributes).get().path));
Matt Spinler5f5352e2020-03-05 16:23:27 -0600296
Matt Spinler1b418862023-06-29 12:37:41 -0500297 lg2::debug("sendNewLogCmd: ID {ID} size {SIZE}", "ID", lg2::hex, id,
298 "SIZE", size);
Matt Spinler5f5352e2020-03-05 16:23:27 -0600299
Matt Spinlerf77debb2019-12-12 10:04:33 -0600300 auto rc = _hostIface->sendNewLogCmd(id, size);
301
302 if (rc == CmdStatus::success)
303 {
304 _inProgressPEL = id;
305 }
306 else
307 {
308 // It failed. Retry
Matt Spinler1b418862023-06-29 12:37:41 -0500309 lg2::error("PLDM send failed, PEL ID = {ID}", "ID", lg2::hex,
310 id);
Matt Spinlerf77debb2019-12-12 10:04:33 -0600311 _pelQueue.push_front(id);
312 _inProgressPEL = 0;
313 _retryTimer.restartOnce(_hostIface->getSendRetryDelay());
314 }
315 }
316 else
317 {
Matt Spinler1b418862023-06-29 12:37:41 -0500318 lg2::error(
319 "PEL ID is not in repository. Cannot notify host. PEL ID = {ID}",
320 "ID", lg2::hex, id);
Matt Spinlerf77debb2019-12-12 10:04:33 -0600321 }
322 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600323}
324
325void HostNotifier::hostStateChange(bool hostUp)
326{
Matt Spinler3019c6f2019-12-11 15:24:45 -0600327 _retryCount = 0;
Matt Spinler41293cb2019-12-12 13:11:09 -0600328 _hostFull = false;
Matt Spinler3019c6f2019-12-11 15:24:45 -0600329
330 if (hostUp && !_pelQueue.empty())
331 {
Matt Spinler1b418862023-06-29 12:37:41 -0500332 lg2::debug("Host state change to on");
Matt Spinlere5f75082022-01-24 16:09:51 -0600333 _hostUpTimer.restartOnce(_hostIface->getHostUpDelay());
Matt Spinler3019c6f2019-12-11 15:24:45 -0600334 }
335 else if (!hostUp)
336 {
Matt Spinler1b418862023-06-29 12:37:41 -0500337 lg2::debug("Host state change to off");
Matt Spinler5f5352e2020-03-05 16:23:27 -0600338
Matt Spinler3019c6f2019-12-11 15:24:45 -0600339 stopCommand();
340
341 // Reset the state on any PELs that were sent but not acked back
342 // to new so they'll get sent again.
343 for (auto id : _sentPELs)
344 {
345 _pelQueue.push_back(id);
346 _repo.setPELHostTransState(id, TransmissionState::newPEL);
347 }
348
349 _sentPELs.clear();
Matt Spinler41293cb2019-12-12 13:11:09 -0600350
351 if (_hostFullTimer.isEnabled())
352 {
353 _hostFullTimer.setEnabled(false);
354 }
Matt Spinlere5f75082022-01-24 16:09:51 -0600355
356 if (_hostUpTimer.isEnabled())
357 {
358 _hostUpTimer.setEnabled(false);
359 }
Matt Spinler3019c6f2019-12-11 15:24:45 -0600360 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600361}
362
363void HostNotifier::commandResponse(ResponseStatus status)
364{
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600365 auto id = _inProgressPEL;
366 _inProgressPEL = 0;
367
368 if (status == ResponseStatus::success)
369 {
Matt Spinler1b418862023-06-29 12:37:41 -0500370 lg2::debug("HostNotifier command response success, PEL ID = {ID}", "ID",
371 lg2::hex, id);
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600372 _retryCount = 0;
373
374 _sentPELs.push_back(id);
375
376 _repo.setPELHostTransState(id, TransmissionState::sent);
377
Matt Spinler41293cb2019-12-12 13:11:09 -0600378 // If the host is full, don't send off the next PEL
379 if (!_hostFull && !_pelQueue.empty())
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600380 {
381 doNewLogNotify();
382 }
383 }
384 else
385 {
Matt Spinler1b418862023-06-29 12:37:41 -0500386 lg2::error("PLDM command response failure, PEL ID = {ID}", "ID",
387 lg2::hex, id);
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600388 // Retry
389 _pelQueue.push_front(id);
390 _retryTimer.restartOnce(_hostIface->getReceiveRetryDelay());
391 }
392}
393
394void HostNotifier::retryTimerExpired()
395{
396 if (_dataIface.isHostUp())
397 {
Matt Spinler1b418862023-06-29 12:37:41 -0500398 lg2::info("Attempting command retry, PEL ID = {ID}", "ID", lg2::hex,
399 _pelQueue.front());
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600400 _retryCount++;
401 doNewLogNotify();
402 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600403}
404
Matt Spinler41293cb2019-12-12 13:11:09 -0600405void HostNotifier::hostFullTimerExpired()
406{
Matt Spinler1b418862023-06-29 12:37:41 -0500407 lg2::debug("Host full timer expired, trying send again");
Matt Spinler41293cb2019-12-12 13:11:09 -0600408 doNewLogNotify();
409}
410
Matt Spinler3019c6f2019-12-11 15:24:45 -0600411void HostNotifier::stopCommand()
412{
413 _retryCount = 0;
414
415 if (_inProgressPEL != 0)
416 {
417 _pelQueue.push_front(_inProgressPEL);
418 _inProgressPEL = 0;
419 }
420
421 if (_retryTimer.isEnabled())
422 {
423 _retryTimer.setEnabled(false);
424 }
425
Matt Spinler829b0522020-03-04 08:38:46 -0600426 // Ensure the PLDM instance ID is released
427 _hostIface->cancelCmd();
Matt Spinler3019c6f2019-12-11 15:24:45 -0600428}
429
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600430void HostNotifier::ackPEL(uint32_t id)
431{
432 _repo.setPELHostTransState(id, TransmissionState::acked);
433
434 // No longer just 'sent', so remove it from the sent list.
435 auto sent = std::find(_sentPELs.begin(), _sentPELs.end(), id);
436 if (sent != _sentPELs.end())
437 {
438 _sentPELs.erase(sent);
439 }
Matt Spinler41293cb2019-12-12 13:11:09 -0600440
441 // An ack means the host is no longer full
442 if (_hostFullTimer.isEnabled())
443 {
444 _hostFullTimer.setEnabled(false);
445 }
446
447 if (_hostFull)
448 {
449 _hostFull = false;
450
Matt Spinler1b418862023-06-29 12:37:41 -0500451 lg2::debug("Host previously full, not anymore after this ack");
Matt Spinler5f5352e2020-03-05 16:23:27 -0600452
Matt Spinler41293cb2019-12-12 13:11:09 -0600453 // Start sending PELs again, from the event loop
454 if (!_pelQueue.empty())
455 {
456 scheduleDispatch();
457 }
458 }
459}
460
461void HostNotifier::setHostFull(uint32_t id)
462{
Matt Spinler610e80f2023-09-12 09:45:01 -0500463 lg2::debug("Received Host full indication, PEL ID = {ID}", "ID", lg2::hex,
464 id);
Matt Spinler41293cb2019-12-12 13:11:09 -0600465
466 _hostFull = true;
467
468 // This PEL needs to get re-sent
469 auto sent = std::find(_sentPELs.begin(), _sentPELs.end(), id);
470 if (sent != _sentPELs.end())
471 {
472 _sentPELs.erase(sent);
473 _repo.setPELHostTransState(id, TransmissionState::newPEL);
474
475 if (std::find(_pelQueue.begin(), _pelQueue.end(), id) ==
476 _pelQueue.end())
477 {
478 _pelQueue.push_front(id);
479 }
480 }
481
482 // The only PELs that will be sent when the
483 // host is full is from this timer callback.
484 if (!_hostFullTimer.isEnabled())
485 {
Matt Spinler1b418862023-06-29 12:37:41 -0500486 lg2::debug("Starting host full timer");
Matt Spinler41293cb2019-12-12 13:11:09 -0600487 _hostFullTimer.restartOnce(_hostIface->getHostFullRetryDelay());
488 }
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600489}
490
Matt Spinlera19b6232019-12-12 13:30:14 -0600491void HostNotifier::setBadPEL(uint32_t id)
492{
Matt Spinler1b418862023-06-29 12:37:41 -0500493 lg2::error("PEL rejected by the host, PEL ID = {ID}", "ID", lg2::hex, id);
Matt Spinlera19b6232019-12-12 13:30:14 -0600494
495 auto sent = std::find(_sentPELs.begin(), _sentPELs.end(), id);
496 if (sent != _sentPELs.end())
497 {
498 _sentPELs.erase(sent);
499 }
500
501 _repo.setPELHostTransState(id, TransmissionState::badPEL);
502}
503
Matt Spinlerf60ac272019-12-11 13:47:50 -0600504} // namespace openpower::pels