blob: 307125b0b3a1f389467bcc3190c33c2409d6fd3b [file] [log] [blame]
Alexander Hansen40fb5492025-10-28 17:56:12 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
Matt Spinler89fa0822019-07-17 13:54:30 -05004#include "repository.hpp"
5
Matt Spinler8a09b982025-05-09 14:09:10 -05006#include "pel_values.hpp"
7
Matt Spinler32a6df62023-01-12 16:30:40 -06008#include <fcntl.h>
Matt Spinlerdd325c32020-07-07 11:01:54 -05009#include <sys/stat.h>
10
Matt Spinlerdb3f2792023-07-07 16:25:26 -050011#include <phosphor-logging/lg2.hpp>
Matt Spinler89fa0822019-07-17 13:54:30 -050012#include <xyz/openbmc_project/Common/File/error.hpp>
13
Patrick Williams2544b412022-10-04 08:41:06 -050014#include <fstream>
15
Matt Spinler89fa0822019-07-17 13:54:30 -050016namespace openpower
17{
18namespace pels
19{
20
21namespace fs = std::filesystem;
Matt Spinler89fa0822019-07-17 13:54:30 -050022namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
23
Matt Spinler7e727a32020-07-07 15:00:17 -050024constexpr size_t warningPercentage = 95;
25
Matt Spinlerdd325c32020-07-07 11:01:54 -050026/**
27 * @brief Returns the amount of space the file uses on disk.
28 *
29 * This is different than just the regular size of the file.
30 *
31 * @param[in] file - The file to get the size of
32 *
33 * @return size_t The disk space the file uses
34 */
35size_t getFileDiskSize(const std::filesystem::path& file)
36{
37 constexpr size_t statBlockSize = 512;
38 struct stat statData;
39 auto rc = stat(file.c_str(), &statData);
40 if (rc != 0)
41 {
42 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -050043 lg2::error("Call to stat() failed on {FILE} with errno {ERRNO}", "FILE",
44 file.native(), "ERRNO", e);
Matt Spinlerdd325c32020-07-07 11:01:54 -050045 abort();
46 }
47
48 return statData.st_blocks * statBlockSize;
49}
50
Matt Spinler8d5f3a22020-07-07 10:30:33 -050051Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
52 size_t maxNumPELs) :
Patrick Williams075c7922024-08-16 15:19:49 -040053 _logPath(basePath / "logs"), _maxRepoSize(repoSize),
54 _maxNumPELs(maxNumPELs), _archivePath(basePath / "logs" / "archive")
Matt Spinler89fa0822019-07-17 13:54:30 -050055{
56 if (!fs::exists(_logPath))
57 {
58 fs::create_directories(_logPath);
59 }
Matt Spinler475e5742019-07-18 16:09:49 -050060
Sumit Kumar1d8835b2021-06-07 09:35:30 -050061 if (!fs::exists(_archivePath))
62 {
63 fs::create_directories(_archivePath);
64 }
65
Matt Spinler475e5742019-07-18 16:09:49 -050066 restore();
67}
68
69void Repository::restore()
70{
71 for (auto& dirEntry : fs::directory_iterator(_logPath))
72 {
73 try
74 {
75 if (!fs::is_regular_file(dirEntry.path()))
76 {
77 continue;
78 }
79
80 std::ifstream file{dirEntry.path()};
81 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
82 std::istreambuf_iterator<char>()};
83 file.close();
84
Matt Spinler07eefc52019-09-26 11:18:26 -050085 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050086 if (pel.valid())
87 {
Matt Spinlera3c12a42019-11-21 13:25:32 -060088 // If the host hasn't acked it, reset the host state so
89 // it will get sent up again.
90 if (pel.hostTransmissionState() == TransmissionState::sent)
91 {
92 pel.setHostTransmissionState(TransmissionState::newPEL);
93 try
94 {
95 write(pel, dirEntry.path());
96 }
Patrick Williams66491c62021-10-06 12:23:37 -050097 catch (const std::exception& e)
Matt Spinlera3c12a42019-11-21 13:25:32 -060098 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -050099 lg2::error(
100 "Failed to save PEL after updating host state, PEL ID = {ID}",
101 "ID", lg2::hex, pel.id());
Matt Spinlera3c12a42019-11-21 13:25:32 -0600102 }
103 }
104
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500105 PELAttributes attributes{
106 dirEntry.path(),
107 getFileDiskSize(dirEntry.path()),
108 pel.privateHeader().creatorID(),
109 pel.userHeader().subsystem(),
110 pel.userHeader().severity(),
111 pel.userHeader().actionFlags(),
112 pel.hostTransmissionState(),
113 pel.hmcTransmissionState(),
114 pel.plid(),
115 pel.getDeconfigFlag(),
116 pel.getGuardFlag(),
117 getMillisecondsSinceEpoch(
118 pel.privateHeader().createTimestamp())};
Matt Spinler0ff00482019-11-06 16:19:46 -0600119
Matt Spinler475e5742019-07-18 16:09:49 -0500120 using pelID = LogID::Pel;
121 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600122 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500123 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600124 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500125
126 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500127 }
128 else
129 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500130 lg2::error(
131 "Found invalid PEL file {FILE} while restoring. Removing.",
132 "FILE", dirEntry.path());
Matt Spinler475e5742019-07-18 16:09:49 -0500133 fs::remove(dirEntry.path());
134 }
135 }
Patrick Williams66491c62021-10-06 12:23:37 -0500136 catch (const std::exception& e)
Matt Spinler475e5742019-07-18 16:09:49 -0500137 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500138 lg2::error("Hit exception while restoring PEL file {FILE}: {ERROR}",
139 "FILE", dirEntry.path(), "ERROR", e);
Matt Spinler475e5742019-07-18 16:09:49 -0500140 }
141 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500142
143 // Get size of archive folder
144 for (auto& dirEntry : fs::directory_iterator(_archivePath))
145 {
146 _archiveSize += getFileDiskSize(dirEntry);
147 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500148}
149
150std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
151{
152 char name[50];
153 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
154 time.yearLSB, time.month, time.day, time.hour, time.minutes,
155 time.seconds, time.hundredths, pelID);
156 return std::string{name};
157}
158
159void Repository::add(std::unique_ptr<PEL>& pel)
160{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600161 pel->setHostTransmissionState(TransmissionState::newPEL);
162 pel->setHMCTransmissionState(TransmissionState::newPEL);
163
Matt Spinler89fa0822019-07-17 13:54:30 -0500164 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600165
166 write(*(pel.get()), path);
167
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500168 PELAttributes attributes{
169 path,
170 getFileDiskSize(path),
171 pel->privateHeader().creatorID(),
172 pel->userHeader().subsystem(),
173 pel->userHeader().severity(),
174 pel->userHeader().actionFlags(),
175 pel->hostTransmissionState(),
176 pel->hmcTransmissionState(),
177 pel->plid(),
178 pel->getDeconfigFlag(),
179 pel->getGuardFlag(),
180 getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600181
182 using pelID = LogID::Pel;
183 using obmcID = LogID::Obmc;
184 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
185 attributes);
186
Matt Spinler44893cc2020-08-26 11:34:17 -0500187 _lastPelID = pel->id();
188
Matt Spinlerb188f782020-07-07 11:18:12 -0500189 updateRepoStats(attributes, true);
190
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600191 processAddCallbacks(*pel);
192}
193
194void Repository::write(const PEL& pel, const fs::path& path)
195{
Matt Spinler89fa0822019-07-17 13:54:30 -0500196 std::ofstream file{path, std::ios::binary};
197
198 if (!file.good())
199 {
200 // If this fails, the filesystem is probably full so it isn't like
201 // we could successfully create yet another error log here.
202 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500203 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500204 lg2::error(
205 "Unable to open PEL file {FILE} for writing, errno = {ERRNO}",
206 "FILE", path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500207 throw file_error::Open();
208 }
209
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600210 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500211 file.write(reinterpret_cast<const char*>(data.data()), data.size());
212
213 if (file.fail())
214 {
215 // Same note as above about not being able to create an error log
216 // for this case even if we wanted.
217 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500218 file.close();
219 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500220 lg2::error("Unable to write PEL file {FILE}, errno = {ERRNO}", "FILE",
221 path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500222 throw file_error::Write();
223 }
Matt Spinler475e5742019-07-18 16:09:49 -0500224}
225
Matt Spinler52602e32020-07-15 12:37:28 -0500226std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500227{
228 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500229 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500230 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500231 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600232 }
Matt Spinler52602e32020-07-15 12:37:28 -0500233
Patrick Williamsff6b5982021-04-22 09:04:17 -0500234 LogID actualID = pel->first;
235 updateRepoStats(pel->second, false);
236
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500237 lg2::debug(
238 "Removing PEL from repository, PEL ID = {PEL_ID}, BMC log ID = {BMC_ID}",
239 "PEL_ID", lg2::hex, actualID.pelID.id, "BMC_ID", actualID.obmcID.id);
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500240
241 if (fs::exists(pel->second.path))
242 {
243 // Check for existense of new archive folder
244 if (!fs::exists(_archivePath))
245 {
246 fs::create_directories(_archivePath);
247 }
248
249 // Move log file to archive folder
250 auto fileName = _archivePath / pel->second.path.filename();
251 fs::rename(pel->second.path, fileName);
252
253 // Update size of file
254 _archiveSize += getFileDiskSize(fileName);
255 }
256
Patrick Williamsff6b5982021-04-22 09:04:17 -0500257 _pelAttributes.erase(pel);
258
259 processDeleteCallbacks(actualID.pelID.id);
260
Matt Spinler52602e32020-07-15 12:37:28 -0500261 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500262}
263
Matt Spinler2813f362019-07-19 12:45:28 -0500264std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
265{
266 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600267 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500268 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600269 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500270 if (!file.good())
271 {
272 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500273 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
274 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler2813f362019-07-19 12:45:28 -0500275 throw file_error::Open();
276 }
277
278 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
279 std::istreambuf_iterator<char>()};
280 return data;
281 }
282
283 return std::nullopt;
284}
285
Matt Spinler6d512242019-12-09 13:44:17 -0600286std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
287{
288 auto pel = findPEL(id);
289 if (pel != _pelAttributes.end())
290 {
Matt Spinler32a6df62023-01-12 16:30:40 -0600291 int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK);
292 if (fd == -1)
Matt Spinler6d512242019-12-09 13:44:17 -0600293 {
294 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500295 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
296 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler6d512242019-12-09 13:44:17 -0600297 throw file_error::Open();
298 }
299
300 // Must leave the file open here. It will be closed by sdbusplus
301 // when it sends it back over D-Bus.
Matt Spinler32a6df62023-01-12 16:30:40 -0600302 return fd;
Matt Spinler6d512242019-12-09 13:44:17 -0600303 }
304 return std::nullopt;
305}
306
Matt Spinler1ea78802019-11-01 13:04:59 -0500307void Repository::for_each(ForEachFunc func) const
308{
Matt Spinler0ff00482019-11-06 16:19:46 -0600309 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500310 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600311 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500312
313 if (!file.good())
314 {
315 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500316 lg2::error(
317 "Repository::for_each: Unable to open PEL file {FILE}, errno = {ERRNO}",
318 "FILE", attributes.path, "ERRNO", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500319 continue;
320 }
321
322 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
323 std::istreambuf_iterator<char>()};
324 file.close();
325
326 PEL pel{data};
327
328 try
329 {
330 if (func(pel))
331 {
332 break;
333 }
334 }
Patrick Williams66491c62021-10-06 12:23:37 -0500335 catch (const std::exception& e)
Matt Spinler1ea78802019-11-01 13:04:59 -0500336 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500337 lg2::error("Repository::for_each function exception: {ERROR}",
338 "ERROR", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500339 }
340 }
341}
342
Matt Spinler421f6532019-11-06 15:40:45 -0600343void Repository::processAddCallbacks(const PEL& pel) const
344{
345 for (auto& [name, func] : _addSubscriptions)
346 {
347 try
348 {
349 func(pel);
350 }
Patrick Williams66491c62021-10-06 12:23:37 -0500351 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600352 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500353 lg2::error(
354 "PEL Repository add callback exception. Name = {NAME}, Error = {ERROR}",
355 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600356 }
357 }
358}
359
360void Repository::processDeleteCallbacks(uint32_t id) const
361{
362 for (auto& [name, func] : _deleteSubscriptions)
363 {
364 try
365 {
366 func(id);
367 }
Patrick Williams66491c62021-10-06 12:23:37 -0500368 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600369 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500370 lg2::error(
371 "PEL Repository delete callback exception. Name = {NAME}, Error = {ERROR}",
372 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600373 }
374 }
375}
376
Matt Spinler0ff00482019-11-06 16:19:46 -0600377std::optional<std::reference_wrapper<const Repository::PELAttributes>>
378 Repository::getPELAttributes(const LogID& id) const
379{
380 auto pel = findPEL(id);
381 if (pel != _pelAttributes.end())
382 {
383 return pel->second;
384 }
385
386 return std::nullopt;
387}
388
Matt Spinler29d18c12019-11-21 13:31:27 -0600389void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
390{
391 LogID id{LogID::Pel{pelID}};
392 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
393 [&id](const auto& a) { return a.first == id; });
394
395 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
396 {
397 PELUpdateFunc func = [state](PEL& pel) {
398 pel.setHostTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500399 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600400 };
401
402 try
403 {
404 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600405 }
Patrick Williams66491c62021-10-06 12:23:37 -0500406 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600407 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500408 lg2::error(
409 "Unable to update PEL host transmission state. Path = {PATH}, Error = {ERROR}",
410 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600411 }
412 }
413}
414
415void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
416{
417 LogID id{LogID::Pel{pelID}};
418 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
419 [&id](const auto& a) { return a.first == id; });
420
421 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
422 {
423 PELUpdateFunc func = [state](PEL& pel) {
424 pel.setHMCTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500425 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600426 };
427
428 try
429 {
430 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600431 }
Patrick Williams66491c62021-10-06 12:23:37 -0500432 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600433 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500434 lg2::error(
435 "Unable to update PEL HMC transmission state. Path = {PATH}, Error = {ERROR}",
436 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600437 }
438 }
439}
440
Matt Spinler1cb59f72023-07-20 09:49:50 -0500441bool Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
Matt Spinler29d18c12019-11-21 13:31:27 -0600442{
443 std::ifstream file{path};
444 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
445 std::istreambuf_iterator<char>()};
446 file.close();
447
448 PEL pel{data};
449
450 if (pel.valid())
451 {
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500452 if (updateFunc(pel))
453 {
454 // Three attribute fields can change post creation from
455 // an updatePEL call:
456 // - hmcTransmissionState - When HMC acks a PEL
457 // - hostTransmissionState - When host acks a PEL
458 // - deconfig flag - Can be cleared for PELs that call out
459 // hotplugged FRUs.
460 // Make sure they're up to date.
461 LogID id{LogID::Pel(pel.id())};
462 auto attr =
463 std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
464 [&id](const auto& a) { return a.first == id; });
465 if (attr != _pelAttributes.end())
466 {
467 attr->second.hmcState = pel.hmcTransmissionState();
468 attr->second.hostState = pel.hostTransmissionState();
469 attr->second.deconfig = pel.getDeconfigFlag();
470 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600471
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500472 write(pel, path);
Matt Spinler1cb59f72023-07-20 09:49:50 -0500473 return true;
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500474 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600475 }
476 else
477 {
478 throw std::runtime_error(
479 "Unable to read a valid PEL when trying to update it");
480 }
Matt Spinler1cb59f72023-07-20 09:49:50 -0500481 return false;
Matt Spinler29d18c12019-11-21 13:31:27 -0600482}
483
Matt Spinlerb188f782020-07-07 11:18:12 -0500484bool Repository::isServiceableSev(const PELAttributes& pel)
485{
486 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
Patrick Williams075c7922024-08-16 15:19:49 -0400487 auto sevPVEntry =
488 pel_values::findByValue(pel.severity, pel_values::severityValues);
Matt Spinlerb188f782020-07-07 11:18:12 -0500489 std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
490
491 bool check1 = (sevType == SeverityType::predictive) ||
492 (sevType == SeverityType::unrecoverable) ||
493 (sevType == SeverityType::critical);
494
495 bool check2 = ((sevType == SeverityType::recovered) ||
496 (sevName == "symptom_recovered")) &&
497 !pel.actionFlags.test(hiddenFlagBit);
498
499 bool check3 = (sevName == "symptom_predictive") ||
500 (sevName == "symptom_unrecoverable") ||
501 (sevName == "symptom_critical");
502
503 return check1 || check2 || check3;
504}
505
506void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
507{
508 auto isServiceable = Repository::isServiceableSev(pel);
509 auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
510
511 auto adjustSize = [pelAdded, &pel](auto& runningSize) {
512 if (pelAdded)
513 {
514 runningSize += pel.sizeOnDisk;
515 }
516 else
517 {
518 runningSize = std::max(static_cast<int64_t>(runningSize) -
519 static_cast<int64_t>(pel.sizeOnDisk),
520 static_cast<int64_t>(0));
521 }
522 };
523
524 adjustSize(_sizes.total);
525
526 if (bmcPEL)
527 {
528 adjustSize(_sizes.bmc);
529 if (isServiceable)
530 {
531 adjustSize(_sizes.bmcServiceable);
532 }
533 else
534 {
535 adjustSize(_sizes.bmcInfo);
536 }
537 }
538 else
539 {
540 adjustSize(_sizes.nonBMC);
541 if (isServiceable)
542 {
543 adjustSize(_sizes.nonBMCServiceable);
544 }
545 else
546 {
547 adjustSize(_sizes.nonBMCInfo);
548 }
549 }
550}
551
Sumit Kumarc2966922021-07-21 10:14:03 -0500552bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500553{
Sumit Kumarc2966922021-07-21 10:14:03 -0500554 std::error_code ec;
555
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500556 if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
557 ((_maxRepoSize * warningPercentage) / 100)))
558 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500559 lg2::info(
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500560 "Repository::sizeWarning function:Deleting the files in archive");
561
Sumit Kumarc2966922021-07-21 10:14:03 -0500562 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500563 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500564 fs::remove(dirEntry.path(), ec);
565 if (ec)
566 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500567 lg2::info("Repository::sizeWarning: Could not delete "
568 "file {FILE} in PEL archive",
569 "FILE", dirEntry.path());
Sumit Kumarc2966922021-07-21 10:14:03 -0500570 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500571 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500572
573 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500574 }
575
Matt Spinler7e727a32020-07-07 15:00:17 -0500576 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
577 (_pelAttributes.size() > _maxNumPELs);
578}
579
Patrick Williams25291152025-02-01 08:21:42 -0500580std::vector<Repository::AttributesReference> Repository::getAllPELAttributes(
581 SortOrder order) const
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500582{
583 std::vector<Repository::AttributesReference> attributes;
584
Patrick Williams075c7922024-08-16 15:19:49 -0400585 std::for_each(_pelAttributes.begin(), _pelAttributes.end(),
586 [&attributes](auto& pelEntry) {
587 attributes.push_back(pelEntry);
588 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500589
590 std::sort(attributes.begin(), attributes.end(),
591 [order](const auto& left, const auto& right) {
Patrick Williams075c7922024-08-16 15:19:49 -0400592 if (order == SortOrder::ascending)
593 {
594 return left.get().second.path < right.get().second.path;
595 }
596 return left.get().second.path > right.get().second.path;
597 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500598
599 return attributes;
600}
601
Patrick Williams25291152025-02-01 08:21:42 -0500602std::vector<uint32_t> Repository::prune(
603 const std::vector<uint32_t>& idsWithHwIsoEntry)
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500604{
605 std::vector<uint32_t> obmcLogIDs;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500606 lg2::info("Pruning PEL repository that takes up {TOTAL} bytes and has "
607 "{NUM_PELS} PELs",
608 "TOTAL", _sizes.total, "NUM_PELS", _pelAttributes.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500609
610 // Set up the 5 functions to check if the PEL category
611 // is still over its limits.
612
613 // BMC informational PELs should only take up 15%
614 IsOverLimitFunc overBMCInfoLimit = [this]() {
615 return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
616 };
617
618 // BMC non informational PELs should only take up 30%
619 IsOverLimitFunc overBMCNonInfoLimit = [this]() {
620 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
621 };
622
623 // Non BMC informational PELs should only take up 15%
624 IsOverLimitFunc overNonBMCInfoLimit = [this]() {
625 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
626 };
627
628 // Non BMC non informational PELs should only take up 15%
629 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
630 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
631 };
632
633 // Bring the total number of PELs down to 80% of the max
634 IsOverLimitFunc tooManyPELsLimit = [this]() {
635 return _pelAttributes.size() > _maxNumPELs * 80 / 100;
636 };
637
638 // Set up the functions to determine which category a PEL is in.
639 // TODO: Return false in these functions if a PEL caused a guard record.
640
641 // A BMC informational PEL
642 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
643 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
644 !Repository::isServiceableSev(pel);
645 };
646
647 // A BMC non informational PEL
648 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
649 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
650 Repository::isServiceableSev(pel);
651 };
652
653 // A non BMC informational PEL
654 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
655 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
656 !Repository::isServiceableSev(pel);
657 };
658
659 // A non BMC non informational PEL
660 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
661 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
662 Repository::isServiceableSev(pel);
663 };
664
665 // When counting PELs, count every PEL
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500666 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500667
668 // Check all 4 categories, which will result in at most 90%
669 // usage (15 + 30 + 15 + 30).
Sumit Kumar027bf282022-01-24 11:25:19 -0600670 removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs);
671 removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry,
672 obmcLogIDs);
673 removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry,
674 obmcLogIDs);
675 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry,
676 obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500677
678 // After the above pruning check if there are still too many PELs,
679 // which can happen depending on PEL sizes.
680 if (_pelAttributes.size() > _maxNumPELs)
681 {
Sumit Kumar027bf282022-01-24 11:25:19 -0600682 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500683 }
684
685 if (!obmcLogIDs.empty())
686 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500687 lg2::info("Number of PELs removed to save space: {NUM_PELS}",
688 "NUM_PELS", obmcLogIDs.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500689 }
690
691 return obmcLogIDs;
692}
693
Matt Spinler45796e82022-07-01 11:25:27 -0500694void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
695 const IsPELTypeFunc& isPELType,
Sumit Kumar027bf282022-01-24 11:25:19 -0600696 const std::vector<uint32_t>& idsWithHwIsoEntry,
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500697 std::vector<uint32_t>& removedBMCLogIDs)
698{
699 if (!isOverLimit())
700 {
701 return;
702 }
703
704 auto attributes = getAllPELAttributes(SortOrder::ascending);
705
706 // Make 4 passes on the PELs, stopping as soon as isOverLimit
707 // returns false.
708 // Pass 1: only delete HMC acked PELs
709 // Pass 2: only delete OS acked PELs
710 // Pass 3: only delete PHYP sent PELs
711 // Pass 4: delete all PELs
712 static const std::vector<std::function<bool(const PELAttributes& pel)>>
713 stateChecks{[](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400714 return pel.hmcState == TransmissionState::acked;
715 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500716
717 [](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400718 return pel.hostState == TransmissionState::acked;
719 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500720
Patrick Williams5fb575a2023-10-20 11:18:21 -0500721 [](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400722 return pel.hostState == TransmissionState::sent;
723 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500724
Patrick Williams5fb575a2023-10-20 11:18:21 -0500725 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500726
727 for (const auto& stateCheck : stateChecks)
728 {
729 for (auto it = attributes.begin(); it != attributes.end();)
730 {
731 const auto& pel = it->get();
732 if (isPELType(pel.second) && stateCheck(pel.second))
733 {
734 auto removedID = pel.first.obmcID.id;
Sumit Kumar027bf282022-01-24 11:25:19 -0600735
736 auto idFound = std::find(idsWithHwIsoEntry.begin(),
737 idsWithHwIsoEntry.end(), removedID);
738 if (idFound != idsWithHwIsoEntry.end())
739 {
740 ++it;
741 continue;
742 }
743
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500744 remove(pel.first);
745
746 removedBMCLogIDs.push_back(removedID);
747
748 attributes.erase(it);
749
750 if (!isOverLimit())
751 {
752 break;
753 }
754 }
755 else
756 {
757 ++it;
758 }
759 }
760
761 if (!isOverLimit())
762 {
763 break;
764 }
765 }
766}
767
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500768void Repository::archivePEL(const PEL& pel)
769{
770 if (pel.valid())
771 {
772 auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime());
773
774 write(pel, path);
775
776 _archiveSize += getFileDiskSize(path);
777 }
778}
779
Matt Spinler89fa0822019-07-17 13:54:30 -0500780} // namespace pels
781} // namespace openpower