blob: 288d39af78f019854b8d84803f5d94c18961a702 [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -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 */
Matt Spinler89fa0822019-07-17 13:54:30 -050016#include "repository.hpp"
17
Matt Spinlerdd325c32020-07-07 11:01:54 -050018#include <sys/stat.h>
19
Matt Spinler89fa0822019-07-17 13:54:30 -050020#include <phosphor-logging/log.hpp>
21#include <xyz/openbmc_project/Common/File/error.hpp>
22
Patrick Williams2544b412022-10-04 08:41:06 -050023#include <fstream>
24
Matt Spinler89fa0822019-07-17 13:54:30 -050025namespace openpower
26{
27namespace pels
28{
29
30namespace fs = std::filesystem;
31using namespace phosphor::logging;
32namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
33
Matt Spinler7e727a32020-07-07 15:00:17 -050034constexpr size_t warningPercentage = 95;
35
Matt Spinlerdd325c32020-07-07 11:01:54 -050036/**
37 * @brief Returns the amount of space the file uses on disk.
38 *
39 * This is different than just the regular size of the file.
40 *
41 * @param[in] file - The file to get the size of
42 *
43 * @return size_t The disk space the file uses
44 */
45size_t getFileDiskSize(const std::filesystem::path& file)
46{
47 constexpr size_t statBlockSize = 512;
48 struct stat statData;
49 auto rc = stat(file.c_str(), &statData);
50 if (rc != 0)
51 {
52 auto e = errno;
53 std::string msg = "call to stat() failed on " + file.native() +
54 " with errno " + std::to_string(e);
55 log<level::ERR>(msg.c_str());
56 abort();
57 }
58
59 return statData.st_blocks * statBlockSize;
60}
61
Matt Spinler8d5f3a22020-07-07 10:30:33 -050062Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
63 size_t maxNumPELs) :
64 _logPath(basePath / "logs"),
Sumit Kumar1d8835b2021-06-07 09:35:30 -050065 _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs),
66 _archivePath(basePath / "logs" / "archive")
Matt Spinler89fa0822019-07-17 13:54:30 -050067{
68 if (!fs::exists(_logPath))
69 {
70 fs::create_directories(_logPath);
71 }
Matt Spinler475e5742019-07-18 16:09:49 -050072
Sumit Kumar1d8835b2021-06-07 09:35:30 -050073 if (!fs::exists(_archivePath))
74 {
75 fs::create_directories(_archivePath);
76 }
77
Matt Spinler475e5742019-07-18 16:09:49 -050078 restore();
79}
80
81void Repository::restore()
82{
83 for (auto& dirEntry : fs::directory_iterator(_logPath))
84 {
85 try
86 {
87 if (!fs::is_regular_file(dirEntry.path()))
88 {
89 continue;
90 }
91
92 std::ifstream file{dirEntry.path()};
93 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
94 std::istreambuf_iterator<char>()};
95 file.close();
96
Matt Spinler07eefc52019-09-26 11:18:26 -050097 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050098 if (pel.valid())
99 {
Matt Spinlera3c12a42019-11-21 13:25:32 -0600100 // If the host hasn't acked it, reset the host state so
101 // it will get sent up again.
102 if (pel.hostTransmissionState() == TransmissionState::sent)
103 {
104 pel.setHostTransmissionState(TransmissionState::newPEL);
105 try
106 {
107 write(pel, dirEntry.path());
108 }
Patrick Williams66491c62021-10-06 12:23:37 -0500109 catch (const std::exception& e)
Matt Spinlera3c12a42019-11-21 13:25:32 -0600110 {
111 log<level::ERR>(
112 "Failed to save PEL after updating host state",
113 entry("PELID=0x%X", pel.id()));
114 }
115 }
116
Matt Spinlerdd325c32020-07-07 11:01:54 -0500117 PELAttributes attributes{dirEntry.path(),
118 getFileDiskSize(dirEntry.path()),
119 pel.privateHeader().creatorID(),
Vijay Loboafb1b462021-07-21 23:29:13 -0500120 pel.userHeader().subsystem(),
Matt Spinlerdd325c32020-07-07 11:01:54 -0500121 pel.userHeader().severity(),
122 pel.userHeader().actionFlags(),
123 pel.hostTransmissionState(),
124 pel.hmcTransmissionState()};
Matt Spinler0ff00482019-11-06 16:19:46 -0600125
Matt Spinler475e5742019-07-18 16:09:49 -0500126 using pelID = LogID::Pel;
127 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600128 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500129 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600130 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500131
132 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500133 }
134 else
135 {
136 log<level::ERR>(
137 "Found invalid PEL file while restoring. Removing.",
138 entry("FILENAME=%s", dirEntry.path().c_str()));
139 fs::remove(dirEntry.path());
140 }
141 }
Patrick Williams66491c62021-10-06 12:23:37 -0500142 catch (const std::exception& e)
Matt Spinler475e5742019-07-18 16:09:49 -0500143 {
144 log<level::ERR>("Hit exception while restoring PEL File",
145 entry("FILENAME=%s", dirEntry.path().c_str()),
146 entry("ERROR=%s", e.what()));
147 }
148 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500149
150 // Get size of archive folder
151 for (auto& dirEntry : fs::directory_iterator(_archivePath))
152 {
153 _archiveSize += getFileDiskSize(dirEntry);
154 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500155}
156
157std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
158{
159 char name[50];
160 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
161 time.yearLSB, time.month, time.day, time.hour, time.minutes,
162 time.seconds, time.hundredths, pelID);
163 return std::string{name};
164}
165
166void Repository::add(std::unique_ptr<PEL>& pel)
167{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600168 pel->setHostTransmissionState(TransmissionState::newPEL);
169 pel->setHMCTransmissionState(TransmissionState::newPEL);
170
Matt Spinler89fa0822019-07-17 13:54:30 -0500171 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600172
173 write(*(pel.get()), path);
174
Matt Spinlerdd325c32020-07-07 11:01:54 -0500175 PELAttributes attributes{path,
176 getFileDiskSize(path),
177 pel->privateHeader().creatorID(),
Vijay Loboafb1b462021-07-21 23:29:13 -0500178 pel->userHeader().subsystem(),
Matt Spinlerdd325c32020-07-07 11:01:54 -0500179 pel->userHeader().severity(),
180 pel->userHeader().actionFlags(),
Matt Spinler346f99a2019-11-21 13:06:35 -0600181 pel->hostTransmissionState(),
182 pel->hmcTransmissionState()};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600183
184 using pelID = LogID::Pel;
185 using obmcID = LogID::Obmc;
186 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
187 attributes);
188
Matt Spinler44893cc2020-08-26 11:34:17 -0500189 _lastPelID = pel->id();
190
Matt Spinlerb188f782020-07-07 11:18:12 -0500191 updateRepoStats(attributes, true);
192
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600193 processAddCallbacks(*pel);
194}
195
196void Repository::write(const PEL& pel, const fs::path& path)
197{
Matt Spinler89fa0822019-07-17 13:54:30 -0500198 std::ofstream file{path, std::ios::binary};
199
200 if (!file.good())
201 {
202 // If this fails, the filesystem is probably full so it isn't like
203 // we could successfully create yet another error log here.
204 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500205 fs::remove(path);
206 log<level::ERR>("Unable to open PEL file for writing",
207 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
208 throw file_error::Open();
209 }
210
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600211 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500212 file.write(reinterpret_cast<const char*>(data.data()), data.size());
213
214 if (file.fail())
215 {
216 // Same note as above about not being able to create an error log
217 // for this case even if we wanted.
218 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500219 file.close();
220 fs::remove(path);
221 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
222 entry("PATH=%s", path.c_str()));
223 throw file_error::Write();
224 }
Matt Spinler475e5742019-07-18 16:09:49 -0500225}
226
Matt Spinler52602e32020-07-15 12:37:28 -0500227std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500228{
229 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500230 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500231 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500232 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600233 }
Matt Spinler52602e32020-07-15 12:37:28 -0500234
Patrick Williamsff6b5982021-04-22 09:04:17 -0500235 LogID actualID = pel->first;
236 updateRepoStats(pel->second, false);
237
238 log<level::DEBUG>("Removing PEL from repository",
239 entry("PEL_ID=0x%X", actualID.pelID.id),
240 entry("OBMC_LOG_ID=%d", actualID.obmcID.id));
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500241
242 if (fs::exists(pel->second.path))
243 {
244 // Check for existense of new archive folder
245 if (!fs::exists(_archivePath))
246 {
247 fs::create_directories(_archivePath);
248 }
249
250 // Move log file to archive folder
251 auto fileName = _archivePath / pel->second.path.filename();
252 fs::rename(pel->second.path, fileName);
253
254 // Update size of file
255 _archiveSize += getFileDiskSize(fileName);
256 }
257
Patrick Williamsff6b5982021-04-22 09:04:17 -0500258 _pelAttributes.erase(pel);
259
260 processDeleteCallbacks(actualID.pelID.id);
261
Matt Spinler52602e32020-07-15 12:37:28 -0500262 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500263}
264
Matt Spinler2813f362019-07-19 12:45:28 -0500265std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
266{
267 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600268 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500269 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600270 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500271 if (!file.good())
272 {
273 auto e = errno;
274 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600275 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500276 throw file_error::Open();
277 }
278
279 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
280 std::istreambuf_iterator<char>()};
281 return data;
282 }
283
284 return std::nullopt;
285}
286
Matt Spinler6d512242019-12-09 13:44:17 -0600287std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
288{
289 auto pel = findPEL(id);
290 if (pel != _pelAttributes.end())
291 {
292 FILE* fp = fopen(pel->second.path.c_str(), "rb");
293
294 if (fp == nullptr)
295 {
296 auto e = errno;
297 log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
298 entry("PATH=%s", pel->second.path.c_str()));
299 throw file_error::Open();
300 }
301
302 // Must leave the file open here. It will be closed by sdbusplus
303 // when it sends it back over D-Bus.
304
305 return fileno(fp);
306 }
307 return std::nullopt;
308}
309
Matt Spinler1ea78802019-11-01 13:04:59 -0500310void Repository::for_each(ForEachFunc func) const
311{
Matt Spinler0ff00482019-11-06 16:19:46 -0600312 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500313 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600314 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500315
316 if (!file.good())
317 {
318 auto e = errno;
319 log<level::ERR>("Repository::for_each: Unable to open PEL file",
320 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600321 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500322 continue;
323 }
324
325 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
326 std::istreambuf_iterator<char>()};
327 file.close();
328
329 PEL pel{data};
330
331 try
332 {
333 if (func(pel))
334 {
335 break;
336 }
337 }
Patrick Williams66491c62021-10-06 12:23:37 -0500338 catch (const std::exception& e)
Matt Spinler1ea78802019-11-01 13:04:59 -0500339 {
340 log<level::ERR>("Repository::for_each function exception",
341 entry("ERROR=%s", e.what()));
342 }
343 }
344}
345
Matt Spinler421f6532019-11-06 15:40:45 -0600346void Repository::processAddCallbacks(const PEL& pel) const
347{
348 for (auto& [name, func] : _addSubscriptions)
349 {
350 try
351 {
352 func(pel);
353 }
Patrick Williams66491c62021-10-06 12:23:37 -0500354 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600355 {
356 log<level::ERR>("PEL Repository add callback exception",
357 entry("NAME=%s", name.c_str()),
358 entry("ERROR=%s", e.what()));
359 }
360 }
361}
362
363void Repository::processDeleteCallbacks(uint32_t id) const
364{
365 for (auto& [name, func] : _deleteSubscriptions)
366 {
367 try
368 {
369 func(id);
370 }
Patrick Williams66491c62021-10-06 12:23:37 -0500371 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600372 {
373 log<level::ERR>("PEL Repository delete callback exception",
374 entry("NAME=%s", name.c_str()),
375 entry("ERROR=%s", e.what()));
376 }
377 }
378}
379
Matt Spinler0ff00482019-11-06 16:19:46 -0600380std::optional<std::reference_wrapper<const Repository::PELAttributes>>
381 Repository::getPELAttributes(const LogID& id) const
382{
383 auto pel = findPEL(id);
384 if (pel != _pelAttributes.end())
385 {
386 return pel->second;
387 }
388
389 return std::nullopt;
390}
391
Matt Spinler29d18c12019-11-21 13:31:27 -0600392void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
393{
394 LogID id{LogID::Pel{pelID}};
395 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
396 [&id](const auto& a) { return a.first == id; });
397
398 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
399 {
400 PELUpdateFunc func = [state](PEL& pel) {
401 pel.setHostTransmissionState(state);
402 };
403
404 try
405 {
406 updatePEL(attr->second.path, func);
407
408 attr->second.hostState = state;
409 }
Patrick Williams66491c62021-10-06 12:23:37 -0500410 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600411 {
412 log<level::ERR>("Unable to update PEL host transmission state",
413 entry("PATH=%s", attr->second.path.c_str()),
414 entry("ERROR=%s", e.what()));
415 }
416 }
417}
418
419void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
420{
421 LogID id{LogID::Pel{pelID}};
422 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
423 [&id](const auto& a) { return a.first == id; });
424
425 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
426 {
427 PELUpdateFunc func = [state](PEL& pel) {
428 pel.setHMCTransmissionState(state);
429 };
430
431 try
432 {
433 updatePEL(attr->second.path, func);
434
435 attr->second.hmcState = state;
436 }
Patrick Williams66491c62021-10-06 12:23:37 -0500437 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600438 {
439 log<level::ERR>("Unable to update PEL HMC transmission state",
440 entry("PATH=%s", attr->second.path.c_str()),
441 entry("ERROR=%s", e.what()));
442 }
443 }
444}
445
446void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
447{
448 std::ifstream file{path};
449 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
450 std::istreambuf_iterator<char>()};
451 file.close();
452
453 PEL pel{data};
454
455 if (pel.valid())
456 {
457 updateFunc(pel);
458
459 write(pel, path);
460 }
461 else
462 {
463 throw std::runtime_error(
464 "Unable to read a valid PEL when trying to update it");
465 }
466}
467
Matt Spinlerb188f782020-07-07 11:18:12 -0500468bool Repository::isServiceableSev(const PELAttributes& pel)
469{
470 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
Patrick Williams2544b412022-10-04 08:41:06 -0500471 auto sevPVEntry = pel_values::findByValue(pel.severity,
472 pel_values::severityValues);
Matt Spinlerb188f782020-07-07 11:18:12 -0500473 std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
474
475 bool check1 = (sevType == SeverityType::predictive) ||
476 (sevType == SeverityType::unrecoverable) ||
477 (sevType == SeverityType::critical);
478
479 bool check2 = ((sevType == SeverityType::recovered) ||
480 (sevName == "symptom_recovered")) &&
481 !pel.actionFlags.test(hiddenFlagBit);
482
483 bool check3 = (sevName == "symptom_predictive") ||
484 (sevName == "symptom_unrecoverable") ||
485 (sevName == "symptom_critical");
486
487 return check1 || check2 || check3;
488}
489
490void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
491{
492 auto isServiceable = Repository::isServiceableSev(pel);
493 auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
494
495 auto adjustSize = [pelAdded, &pel](auto& runningSize) {
496 if (pelAdded)
497 {
498 runningSize += pel.sizeOnDisk;
499 }
500 else
501 {
502 runningSize = std::max(static_cast<int64_t>(runningSize) -
503 static_cast<int64_t>(pel.sizeOnDisk),
504 static_cast<int64_t>(0));
505 }
506 };
507
508 adjustSize(_sizes.total);
509
510 if (bmcPEL)
511 {
512 adjustSize(_sizes.bmc);
513 if (isServiceable)
514 {
515 adjustSize(_sizes.bmcServiceable);
516 }
517 else
518 {
519 adjustSize(_sizes.bmcInfo);
520 }
521 }
522 else
523 {
524 adjustSize(_sizes.nonBMC);
525 if (isServiceable)
526 {
527 adjustSize(_sizes.nonBMCServiceable);
528 }
529 else
530 {
531 adjustSize(_sizes.nonBMCInfo);
532 }
533 }
534}
535
Sumit Kumarc2966922021-07-21 10:14:03 -0500536bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500537{
Sumit Kumarc2966922021-07-21 10:14:03 -0500538 std::error_code ec;
539
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500540 if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
541 ((_maxRepoSize * warningPercentage) / 100)))
542 {
543 log<level::INFO>(
544 "Repository::sizeWarning function:Deleting the files in archive");
545
Sumit Kumarc2966922021-07-21 10:14:03 -0500546 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500547 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500548 fs::remove(dirEntry.path(), ec);
549 if (ec)
550 {
551 log<level::INFO>(
552 "Repository::sizeWarning function:Could not delete "
553 "a file in PEL archive",
554 entry("FILENAME=%s", dirEntry.path().c_str()));
555 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500556 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500557
558 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500559 }
560
Matt Spinler7e727a32020-07-07 15:00:17 -0500561 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
562 (_pelAttributes.size() > _maxNumPELs);
563}
564
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500565std::vector<Repository::AttributesReference>
566 Repository::getAllPELAttributes(SortOrder order) const
567{
568 std::vector<Repository::AttributesReference> attributes;
569
570 std::for_each(
571 _pelAttributes.begin(), _pelAttributes.end(),
572 [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); });
573
574 std::sort(attributes.begin(), attributes.end(),
575 [order](const auto& left, const auto& right) {
576 if (order == SortOrder::ascending)
577 {
578 return left.get().second.path < right.get().second.path;
579 }
580 return left.get().second.path > right.get().second.path;
581 });
582
583 return attributes;
584}
585
Sumit Kumar027bf282022-01-24 11:25:19 -0600586std::vector<uint32_t>
587 Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry)
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500588{
589 std::vector<uint32_t> obmcLogIDs;
590 std::string msg = "Pruning PEL repository that takes up " +
591 std::to_string(_sizes.total) + " bytes and has " +
592 std::to_string(_pelAttributes.size()) + " PELs";
593 log<level::INFO>(msg.c_str());
594
595 // Set up the 5 functions to check if the PEL category
596 // is still over its limits.
597
598 // BMC informational PELs should only take up 15%
599 IsOverLimitFunc overBMCInfoLimit = [this]() {
600 return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
601 };
602
603 // BMC non informational PELs should only take up 30%
604 IsOverLimitFunc overBMCNonInfoLimit = [this]() {
605 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
606 };
607
608 // Non BMC informational PELs should only take up 15%
609 IsOverLimitFunc overNonBMCInfoLimit = [this]() {
610 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
611 };
612
613 // Non BMC non informational PELs should only take up 15%
614 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
615 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
616 };
617
618 // Bring the total number of PELs down to 80% of the max
619 IsOverLimitFunc tooManyPELsLimit = [this]() {
620 return _pelAttributes.size() > _maxNumPELs * 80 / 100;
621 };
622
623 // Set up the functions to determine which category a PEL is in.
624 // TODO: Return false in these functions if a PEL caused a guard record.
625
626 // A BMC informational PEL
627 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
628 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
629 !Repository::isServiceableSev(pel);
630 };
631
632 // A BMC non informational PEL
633 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
634 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
635 Repository::isServiceableSev(pel);
636 };
637
638 // A non BMC informational PEL
639 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
640 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
641 !Repository::isServiceableSev(pel);
642 };
643
644 // A non BMC non informational PEL
645 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
646 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
647 Repository::isServiceableSev(pel);
648 };
649
650 // When counting PELs, count every PEL
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500651 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500652
653 // Check all 4 categories, which will result in at most 90%
654 // usage (15 + 30 + 15 + 30).
Sumit Kumar027bf282022-01-24 11:25:19 -0600655 removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs);
656 removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry,
657 obmcLogIDs);
658 removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry,
659 obmcLogIDs);
660 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry,
661 obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500662
663 // After the above pruning check if there are still too many PELs,
664 // which can happen depending on PEL sizes.
665 if (_pelAttributes.size() > _maxNumPELs)
666 {
Sumit Kumar027bf282022-01-24 11:25:19 -0600667 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500668 }
669
670 if (!obmcLogIDs.empty())
671 {
Matt Spinler45796e82022-07-01 11:25:27 -0500672 std::string m = "Number of PELs removed to save space: " +
673 std::to_string(obmcLogIDs.size());
674 log<level::INFO>(m.c_str());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500675 }
676
677 return obmcLogIDs;
678}
679
Matt Spinler45796e82022-07-01 11:25:27 -0500680void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
681 const IsPELTypeFunc& isPELType,
Sumit Kumar027bf282022-01-24 11:25:19 -0600682 const std::vector<uint32_t>& idsWithHwIsoEntry,
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500683 std::vector<uint32_t>& removedBMCLogIDs)
684{
685 if (!isOverLimit())
686 {
687 return;
688 }
689
690 auto attributes = getAllPELAttributes(SortOrder::ascending);
691
692 // Make 4 passes on the PELs, stopping as soon as isOverLimit
693 // returns false.
694 // Pass 1: only delete HMC acked PELs
695 // Pass 2: only delete OS acked PELs
696 // Pass 3: only delete PHYP sent PELs
697 // Pass 4: delete all PELs
698 static const std::vector<std::function<bool(const PELAttributes& pel)>>
699 stateChecks{[](const auto& pel) {
700 return pel.hmcState == TransmissionState::acked;
701 },
702
703 [](const auto& pel) {
704 return pel.hostState == TransmissionState::acked;
705 },
706
707 [](const auto& pel) {
708 return pel.hostState == TransmissionState::sent;
709 },
710
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500711 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500712
713 for (const auto& stateCheck : stateChecks)
714 {
715 for (auto it = attributes.begin(); it != attributes.end();)
716 {
717 const auto& pel = it->get();
718 if (isPELType(pel.second) && stateCheck(pel.second))
719 {
720 auto removedID = pel.first.obmcID.id;
Sumit Kumar027bf282022-01-24 11:25:19 -0600721
722 auto idFound = std::find(idsWithHwIsoEntry.begin(),
723 idsWithHwIsoEntry.end(), removedID);
724 if (idFound != idsWithHwIsoEntry.end())
725 {
726 ++it;
727 continue;
728 }
729
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500730 remove(pel.first);
731
732 removedBMCLogIDs.push_back(removedID);
733
734 attributes.erase(it);
735
736 if (!isOverLimit())
737 {
738 break;
739 }
740 }
741 else
742 {
743 ++it;
744 }
745 }
746
747 if (!isOverLimit())
748 {
749 break;
750 }
751 }
752}
753
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500754void Repository::archivePEL(const PEL& pel)
755{
756 if (pel.valid())
757 {
758 auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime());
759
760 write(pel, path);
761
762 _archiveSize += getFileDiskSize(path);
763 }
764}
765
Matt Spinler89fa0822019-07-17 13:54:30 -0500766} // namespace pels
767} // namespace openpower