blob: a5a0b8ca97e6937e7f994e392b03bff5d186932e [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 Spinler32a6df62023-01-12 16:30:40 -060018#include <fcntl.h>
Matt Spinlerdd325c32020-07-07 11:01:54 -050019#include <sys/stat.h>
20
Matt Spinlerdb3f2792023-07-07 16:25:26 -050021#include <phosphor-logging/lg2.hpp>
Matt Spinler89fa0822019-07-17 13:54:30 -050022#include <xyz/openbmc_project/Common/File/error.hpp>
23
Patrick Williams2544b412022-10-04 08:41:06 -050024#include <fstream>
25
Matt Spinler89fa0822019-07-17 13:54:30 -050026namespace openpower
27{
28namespace pels
29{
30
31namespace fs = std::filesystem;
Matt Spinler89fa0822019-07-17 13:54:30 -050032namespace 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;
Matt Spinlerdb3f2792023-07-07 16:25:26 -050053 lg2::error("Call to stat() failed on {FILE} with errno {ERRNO}", "FILE",
54 file.native(), "ERRNO", e);
Matt Spinlerdd325c32020-07-07 11:01:54 -050055 abort();
56 }
57
58 return statData.st_blocks * statBlockSize;
59}
60
Matt Spinler8d5f3a22020-07-07 10:30:33 -050061Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
62 size_t maxNumPELs) :
Patrick Williams075c7922024-08-16 15:19:49 -040063 _logPath(basePath / "logs"), _maxRepoSize(repoSize),
64 _maxNumPELs(maxNumPELs), _archivePath(basePath / "logs" / "archive")
Matt Spinler89fa0822019-07-17 13:54:30 -050065{
66 if (!fs::exists(_logPath))
67 {
68 fs::create_directories(_logPath);
69 }
Matt Spinler475e5742019-07-18 16:09:49 -050070
Sumit Kumar1d8835b2021-06-07 09:35:30 -050071 if (!fs::exists(_archivePath))
72 {
73 fs::create_directories(_archivePath);
74 }
75
Matt Spinler475e5742019-07-18 16:09:49 -050076 restore();
77}
78
79void Repository::restore()
80{
81 for (auto& dirEntry : fs::directory_iterator(_logPath))
82 {
83 try
84 {
85 if (!fs::is_regular_file(dirEntry.path()))
86 {
87 continue;
88 }
89
90 std::ifstream file{dirEntry.path()};
91 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
92 std::istreambuf_iterator<char>()};
93 file.close();
94
Matt Spinler07eefc52019-09-26 11:18:26 -050095 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050096 if (pel.valid())
97 {
Matt Spinlera3c12a42019-11-21 13:25:32 -060098 // If the host hasn't acked it, reset the host state so
99 // it will get sent up again.
100 if (pel.hostTransmissionState() == TransmissionState::sent)
101 {
102 pel.setHostTransmissionState(TransmissionState::newPEL);
103 try
104 {
105 write(pel, dirEntry.path());
106 }
Patrick Williams66491c62021-10-06 12:23:37 -0500107 catch (const std::exception& e)
Matt Spinlera3c12a42019-11-21 13:25:32 -0600108 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500109 lg2::error(
110 "Failed to save PEL after updating host state, PEL ID = {ID}",
111 "ID", lg2::hex, pel.id());
Matt Spinlera3c12a42019-11-21 13:25:32 -0600112 }
113 }
114
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500115 PELAttributes attributes{
116 dirEntry.path(),
117 getFileDiskSize(dirEntry.path()),
118 pel.privateHeader().creatorID(),
119 pel.userHeader().subsystem(),
120 pel.userHeader().severity(),
121 pel.userHeader().actionFlags(),
122 pel.hostTransmissionState(),
123 pel.hmcTransmissionState(),
124 pel.plid(),
125 pel.getDeconfigFlag(),
126 pel.getGuardFlag(),
127 getMillisecondsSinceEpoch(
128 pel.privateHeader().createTimestamp())};
Matt Spinler0ff00482019-11-06 16:19:46 -0600129
Matt Spinler475e5742019-07-18 16:09:49 -0500130 using pelID = LogID::Pel;
131 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600132 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500133 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600134 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500135
136 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500137 }
138 else
139 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500140 lg2::error(
141 "Found invalid PEL file {FILE} while restoring. Removing.",
142 "FILE", dirEntry.path());
Matt Spinler475e5742019-07-18 16:09:49 -0500143 fs::remove(dirEntry.path());
144 }
145 }
Patrick Williams66491c62021-10-06 12:23:37 -0500146 catch (const std::exception& e)
Matt Spinler475e5742019-07-18 16:09:49 -0500147 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500148 lg2::error("Hit exception while restoring PEL file {FILE}: {ERROR}",
149 "FILE", dirEntry.path(), "ERROR", e);
Matt Spinler475e5742019-07-18 16:09:49 -0500150 }
151 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500152
153 // Get size of archive folder
154 for (auto& dirEntry : fs::directory_iterator(_archivePath))
155 {
156 _archiveSize += getFileDiskSize(dirEntry);
157 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500158}
159
160std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
161{
162 char name[50];
163 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
164 time.yearLSB, time.month, time.day, time.hour, time.minutes,
165 time.seconds, time.hundredths, pelID);
166 return std::string{name};
167}
168
169void Repository::add(std::unique_ptr<PEL>& pel)
170{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600171 pel->setHostTransmissionState(TransmissionState::newPEL);
172 pel->setHMCTransmissionState(TransmissionState::newPEL);
173
Matt Spinler89fa0822019-07-17 13:54:30 -0500174 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600175
176 write(*(pel.get()), path);
177
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500178 PELAttributes attributes{
179 path,
180 getFileDiskSize(path),
181 pel->privateHeader().creatorID(),
182 pel->userHeader().subsystem(),
183 pel->userHeader().severity(),
184 pel->userHeader().actionFlags(),
185 pel->hostTransmissionState(),
186 pel->hmcTransmissionState(),
187 pel->plid(),
188 pel->getDeconfigFlag(),
189 pel->getGuardFlag(),
190 getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600191
192 using pelID = LogID::Pel;
193 using obmcID = LogID::Obmc;
194 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
195 attributes);
196
Matt Spinler44893cc2020-08-26 11:34:17 -0500197 _lastPelID = pel->id();
198
Matt Spinlerb188f782020-07-07 11:18:12 -0500199 updateRepoStats(attributes, true);
200
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600201 processAddCallbacks(*pel);
202}
203
204void Repository::write(const PEL& pel, const fs::path& path)
205{
Matt Spinler89fa0822019-07-17 13:54:30 -0500206 std::ofstream file{path, std::ios::binary};
207
208 if (!file.good())
209 {
210 // If this fails, the filesystem is probably full so it isn't like
211 // we could successfully create yet another error log here.
212 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500213 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500214 lg2::error(
215 "Unable to open PEL file {FILE} for writing, errno = {ERRNO}",
216 "FILE", path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500217 throw file_error::Open();
218 }
219
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600220 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500221 file.write(reinterpret_cast<const char*>(data.data()), data.size());
222
223 if (file.fail())
224 {
225 // Same note as above about not being able to create an error log
226 // for this case even if we wanted.
227 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500228 file.close();
229 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500230 lg2::error("Unable to write PEL file {FILE}, errno = {ERRNO}", "FILE",
231 path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500232 throw file_error::Write();
233 }
Matt Spinler475e5742019-07-18 16:09:49 -0500234}
235
Matt Spinler52602e32020-07-15 12:37:28 -0500236std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500237{
238 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500239 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500240 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500241 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600242 }
Matt Spinler52602e32020-07-15 12:37:28 -0500243
Patrick Williamsff6b5982021-04-22 09:04:17 -0500244 LogID actualID = pel->first;
245 updateRepoStats(pel->second, false);
246
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500247 lg2::debug(
248 "Removing PEL from repository, PEL ID = {PEL_ID}, BMC log ID = {BMC_ID}",
249 "PEL_ID", lg2::hex, actualID.pelID.id, "BMC_ID", actualID.obmcID.id);
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500250
251 if (fs::exists(pel->second.path))
252 {
253 // Check for existense of new archive folder
254 if (!fs::exists(_archivePath))
255 {
256 fs::create_directories(_archivePath);
257 }
258
259 // Move log file to archive folder
260 auto fileName = _archivePath / pel->second.path.filename();
261 fs::rename(pel->second.path, fileName);
262
263 // Update size of file
264 _archiveSize += getFileDiskSize(fileName);
265 }
266
Patrick Williamsff6b5982021-04-22 09:04:17 -0500267 _pelAttributes.erase(pel);
268
269 processDeleteCallbacks(actualID.pelID.id);
270
Matt Spinler52602e32020-07-15 12:37:28 -0500271 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500272}
273
Matt Spinler2813f362019-07-19 12:45:28 -0500274std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
275{
276 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600277 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500278 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600279 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500280 if (!file.good())
281 {
282 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500283 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
284 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler2813f362019-07-19 12:45:28 -0500285 throw file_error::Open();
286 }
287
288 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
289 std::istreambuf_iterator<char>()};
290 return data;
291 }
292
293 return std::nullopt;
294}
295
Matt Spinler6d512242019-12-09 13:44:17 -0600296std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
297{
298 auto pel = findPEL(id);
299 if (pel != _pelAttributes.end())
300 {
Matt Spinler32a6df62023-01-12 16:30:40 -0600301 int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK);
302 if (fd == -1)
Matt Spinler6d512242019-12-09 13:44:17 -0600303 {
304 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500305 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
306 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler6d512242019-12-09 13:44:17 -0600307 throw file_error::Open();
308 }
309
310 // Must leave the file open here. It will be closed by sdbusplus
311 // when it sends it back over D-Bus.
Matt Spinler32a6df62023-01-12 16:30:40 -0600312 return fd;
Matt Spinler6d512242019-12-09 13:44:17 -0600313 }
314 return std::nullopt;
315}
316
Matt Spinler1ea78802019-11-01 13:04:59 -0500317void Repository::for_each(ForEachFunc func) const
318{
Matt Spinler0ff00482019-11-06 16:19:46 -0600319 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500320 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600321 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500322
323 if (!file.good())
324 {
325 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500326 lg2::error(
327 "Repository::for_each: Unable to open PEL file {FILE}, errno = {ERRNO}",
328 "FILE", attributes.path, "ERRNO", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500329 continue;
330 }
331
332 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
333 std::istreambuf_iterator<char>()};
334 file.close();
335
336 PEL pel{data};
337
338 try
339 {
340 if (func(pel))
341 {
342 break;
343 }
344 }
Patrick Williams66491c62021-10-06 12:23:37 -0500345 catch (const std::exception& e)
Matt Spinler1ea78802019-11-01 13:04:59 -0500346 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500347 lg2::error("Repository::for_each function exception: {ERROR}",
348 "ERROR", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500349 }
350 }
351}
352
Matt Spinler421f6532019-11-06 15:40:45 -0600353void Repository::processAddCallbacks(const PEL& pel) const
354{
355 for (auto& [name, func] : _addSubscriptions)
356 {
357 try
358 {
359 func(pel);
360 }
Patrick Williams66491c62021-10-06 12:23:37 -0500361 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600362 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500363 lg2::error(
364 "PEL Repository add callback exception. Name = {NAME}, Error = {ERROR}",
365 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600366 }
367 }
368}
369
370void Repository::processDeleteCallbacks(uint32_t id) const
371{
372 for (auto& [name, func] : _deleteSubscriptions)
373 {
374 try
375 {
376 func(id);
377 }
Patrick Williams66491c62021-10-06 12:23:37 -0500378 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600379 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500380 lg2::error(
381 "PEL Repository delete callback exception. Name = {NAME}, Error = {ERROR}",
382 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600383 }
384 }
385}
386
Matt Spinler0ff00482019-11-06 16:19:46 -0600387std::optional<std::reference_wrapper<const Repository::PELAttributes>>
388 Repository::getPELAttributes(const LogID& id) const
389{
390 auto pel = findPEL(id);
391 if (pel != _pelAttributes.end())
392 {
393 return pel->second;
394 }
395
396 return std::nullopt;
397}
398
Matt Spinler29d18c12019-11-21 13:31:27 -0600399void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
400{
401 LogID id{LogID::Pel{pelID}};
402 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
403 [&id](const auto& a) { return a.first == id; });
404
405 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
406 {
407 PELUpdateFunc func = [state](PEL& pel) {
408 pel.setHostTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500409 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600410 };
411
412 try
413 {
414 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600415 }
Patrick Williams66491c62021-10-06 12:23:37 -0500416 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600417 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500418 lg2::error(
419 "Unable to update PEL host transmission state. Path = {PATH}, Error = {ERROR}",
420 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600421 }
422 }
423}
424
425void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
426{
427 LogID id{LogID::Pel{pelID}};
428 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
429 [&id](const auto& a) { return a.first == id; });
430
431 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
432 {
433 PELUpdateFunc func = [state](PEL& pel) {
434 pel.setHMCTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500435 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600436 };
437
438 try
439 {
440 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600441 }
Patrick Williams66491c62021-10-06 12:23:37 -0500442 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600443 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500444 lg2::error(
445 "Unable to update PEL HMC transmission state. Path = {PATH}, Error = {ERROR}",
446 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600447 }
448 }
449}
450
Matt Spinler1cb59f72023-07-20 09:49:50 -0500451bool Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
Matt Spinler29d18c12019-11-21 13:31:27 -0600452{
453 std::ifstream file{path};
454 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
455 std::istreambuf_iterator<char>()};
456 file.close();
457
458 PEL pel{data};
459
460 if (pel.valid())
461 {
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500462 if (updateFunc(pel))
463 {
464 // Three attribute fields can change post creation from
465 // an updatePEL call:
466 // - hmcTransmissionState - When HMC acks a PEL
467 // - hostTransmissionState - When host acks a PEL
468 // - deconfig flag - Can be cleared for PELs that call out
469 // hotplugged FRUs.
470 // Make sure they're up to date.
471 LogID id{LogID::Pel(pel.id())};
472 auto attr =
473 std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
474 [&id](const auto& a) { return a.first == id; });
475 if (attr != _pelAttributes.end())
476 {
477 attr->second.hmcState = pel.hmcTransmissionState();
478 attr->second.hostState = pel.hostTransmissionState();
479 attr->second.deconfig = pel.getDeconfigFlag();
480 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600481
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500482 write(pel, path);
Matt Spinler1cb59f72023-07-20 09:49:50 -0500483 return true;
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500484 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600485 }
486 else
487 {
488 throw std::runtime_error(
489 "Unable to read a valid PEL when trying to update it");
490 }
Matt Spinler1cb59f72023-07-20 09:49:50 -0500491 return false;
Matt Spinler29d18c12019-11-21 13:31:27 -0600492}
493
Matt Spinlerb188f782020-07-07 11:18:12 -0500494bool Repository::isServiceableSev(const PELAttributes& pel)
495{
496 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
Patrick Williams075c7922024-08-16 15:19:49 -0400497 auto sevPVEntry =
498 pel_values::findByValue(pel.severity, pel_values::severityValues);
Matt Spinlerb188f782020-07-07 11:18:12 -0500499 std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
500
501 bool check1 = (sevType == SeverityType::predictive) ||
502 (sevType == SeverityType::unrecoverable) ||
503 (sevType == SeverityType::critical);
504
505 bool check2 = ((sevType == SeverityType::recovered) ||
506 (sevName == "symptom_recovered")) &&
507 !pel.actionFlags.test(hiddenFlagBit);
508
509 bool check3 = (sevName == "symptom_predictive") ||
510 (sevName == "symptom_unrecoverable") ||
511 (sevName == "symptom_critical");
512
513 return check1 || check2 || check3;
514}
515
516void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
517{
518 auto isServiceable = Repository::isServiceableSev(pel);
519 auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
520
521 auto adjustSize = [pelAdded, &pel](auto& runningSize) {
522 if (pelAdded)
523 {
524 runningSize += pel.sizeOnDisk;
525 }
526 else
527 {
528 runningSize = std::max(static_cast<int64_t>(runningSize) -
529 static_cast<int64_t>(pel.sizeOnDisk),
530 static_cast<int64_t>(0));
531 }
532 };
533
534 adjustSize(_sizes.total);
535
536 if (bmcPEL)
537 {
538 adjustSize(_sizes.bmc);
539 if (isServiceable)
540 {
541 adjustSize(_sizes.bmcServiceable);
542 }
543 else
544 {
545 adjustSize(_sizes.bmcInfo);
546 }
547 }
548 else
549 {
550 adjustSize(_sizes.nonBMC);
551 if (isServiceable)
552 {
553 adjustSize(_sizes.nonBMCServiceable);
554 }
555 else
556 {
557 adjustSize(_sizes.nonBMCInfo);
558 }
559 }
560}
561
Sumit Kumarc2966922021-07-21 10:14:03 -0500562bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500563{
Sumit Kumarc2966922021-07-21 10:14:03 -0500564 std::error_code ec;
565
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500566 if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
567 ((_maxRepoSize * warningPercentage) / 100)))
568 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500569 lg2::info(
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500570 "Repository::sizeWarning function:Deleting the files in archive");
571
Sumit Kumarc2966922021-07-21 10:14:03 -0500572 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500573 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500574 fs::remove(dirEntry.path(), ec);
575 if (ec)
576 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500577 lg2::info("Repository::sizeWarning: Could not delete "
578 "file {FILE} in PEL archive",
579 "FILE", dirEntry.path());
Sumit Kumarc2966922021-07-21 10:14:03 -0500580 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500581 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500582
583 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500584 }
585
Matt Spinler7e727a32020-07-07 15:00:17 -0500586 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
587 (_pelAttributes.size() > _maxNumPELs);
588}
589
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500590std::vector<Repository::AttributesReference>
591 Repository::getAllPELAttributes(SortOrder order) const
592{
593 std::vector<Repository::AttributesReference> attributes;
594
Patrick Williams075c7922024-08-16 15:19:49 -0400595 std::for_each(_pelAttributes.begin(), _pelAttributes.end(),
596 [&attributes](auto& pelEntry) {
597 attributes.push_back(pelEntry);
598 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500599
600 std::sort(attributes.begin(), attributes.end(),
601 [order](const auto& left, const auto& right) {
Patrick Williams075c7922024-08-16 15:19:49 -0400602 if (order == SortOrder::ascending)
603 {
604 return left.get().second.path < right.get().second.path;
605 }
606 return left.get().second.path > right.get().second.path;
607 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500608
609 return attributes;
610}
611
Sumit Kumar027bf282022-01-24 11:25:19 -0600612std::vector<uint32_t>
613 Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry)
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500614{
615 std::vector<uint32_t> obmcLogIDs;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500616 lg2::info("Pruning PEL repository that takes up {TOTAL} bytes and has "
617 "{NUM_PELS} PELs",
618 "TOTAL", _sizes.total, "NUM_PELS", _pelAttributes.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500619
620 // Set up the 5 functions to check if the PEL category
621 // is still over its limits.
622
623 // BMC informational PELs should only take up 15%
624 IsOverLimitFunc overBMCInfoLimit = [this]() {
625 return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
626 };
627
628 // BMC non informational PELs should only take up 30%
629 IsOverLimitFunc overBMCNonInfoLimit = [this]() {
630 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
631 };
632
633 // Non BMC informational PELs should only take up 15%
634 IsOverLimitFunc overNonBMCInfoLimit = [this]() {
635 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
636 };
637
638 // Non BMC non informational PELs should only take up 15%
639 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
640 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
641 };
642
643 // Bring the total number of PELs down to 80% of the max
644 IsOverLimitFunc tooManyPELsLimit = [this]() {
645 return _pelAttributes.size() > _maxNumPELs * 80 / 100;
646 };
647
648 // Set up the functions to determine which category a PEL is in.
649 // TODO: Return false in these functions if a PEL caused a guard record.
650
651 // A BMC informational PEL
652 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
653 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
654 !Repository::isServiceableSev(pel);
655 };
656
657 // A BMC non informational PEL
658 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
659 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
660 Repository::isServiceableSev(pel);
661 };
662
663 // A non BMC informational PEL
664 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
665 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
666 !Repository::isServiceableSev(pel);
667 };
668
669 // A non BMC non informational PEL
670 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
671 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
672 Repository::isServiceableSev(pel);
673 };
674
675 // When counting PELs, count every PEL
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500676 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500677
678 // Check all 4 categories, which will result in at most 90%
679 // usage (15 + 30 + 15 + 30).
Sumit Kumar027bf282022-01-24 11:25:19 -0600680 removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs);
681 removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry,
682 obmcLogIDs);
683 removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry,
684 obmcLogIDs);
685 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry,
686 obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500687
688 // After the above pruning check if there are still too many PELs,
689 // which can happen depending on PEL sizes.
690 if (_pelAttributes.size() > _maxNumPELs)
691 {
Sumit Kumar027bf282022-01-24 11:25:19 -0600692 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500693 }
694
695 if (!obmcLogIDs.empty())
696 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500697 lg2::info("Number of PELs removed to save space: {NUM_PELS}",
698 "NUM_PELS", obmcLogIDs.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500699 }
700
701 return obmcLogIDs;
702}
703
Matt Spinler45796e82022-07-01 11:25:27 -0500704void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
705 const IsPELTypeFunc& isPELType,
Sumit Kumar027bf282022-01-24 11:25:19 -0600706 const std::vector<uint32_t>& idsWithHwIsoEntry,
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500707 std::vector<uint32_t>& removedBMCLogIDs)
708{
709 if (!isOverLimit())
710 {
711 return;
712 }
713
714 auto attributes = getAllPELAttributes(SortOrder::ascending);
715
716 // Make 4 passes on the PELs, stopping as soon as isOverLimit
717 // returns false.
718 // Pass 1: only delete HMC acked PELs
719 // Pass 2: only delete OS acked PELs
720 // Pass 3: only delete PHYP sent PELs
721 // Pass 4: delete all PELs
722 static const std::vector<std::function<bool(const PELAttributes& pel)>>
723 stateChecks{[](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400724 return pel.hmcState == TransmissionState::acked;
725 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500726
727 [](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400728 return pel.hostState == TransmissionState::acked;
729 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500730
Patrick Williams5fb575a2023-10-20 11:18:21 -0500731 [](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400732 return pel.hostState == TransmissionState::sent;
733 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500734
Patrick Williams5fb575a2023-10-20 11:18:21 -0500735 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500736
737 for (const auto& stateCheck : stateChecks)
738 {
739 for (auto it = attributes.begin(); it != attributes.end();)
740 {
741 const auto& pel = it->get();
742 if (isPELType(pel.second) && stateCheck(pel.second))
743 {
744 auto removedID = pel.first.obmcID.id;
Sumit Kumar027bf282022-01-24 11:25:19 -0600745
746 auto idFound = std::find(idsWithHwIsoEntry.begin(),
747 idsWithHwIsoEntry.end(), removedID);
748 if (idFound != idsWithHwIsoEntry.end())
749 {
750 ++it;
751 continue;
752 }
753
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500754 remove(pel.first);
755
756 removedBMCLogIDs.push_back(removedID);
757
758 attributes.erase(it);
759
760 if (!isOverLimit())
761 {
762 break;
763 }
764 }
765 else
766 {
767 ++it;
768 }
769 }
770
771 if (!isOverLimit())
772 {
773 break;
774 }
775 }
776}
777
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500778void Repository::archivePEL(const PEL& pel)
779{
780 if (pel.valid())
781 {
782 auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime());
783
784 write(pel, path);
785
786 _archiveSize += getFileDiskSize(path);
787 }
788}
789
Matt Spinler89fa0822019-07-17 13:54:30 -0500790} // namespace pels
791} // namespace openpower