blob: 6335a3777c22006a70a68653264545fee9cef233 [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 Spinler8a09b982025-05-09 14:09:10 -050018#include "pel_values.hpp"
19
Matt Spinler32a6df62023-01-12 16:30:40 -060020#include <fcntl.h>
Matt Spinlerdd325c32020-07-07 11:01:54 -050021#include <sys/stat.h>
22
Matt Spinlerdb3f2792023-07-07 16:25:26 -050023#include <phosphor-logging/lg2.hpp>
Matt Spinler89fa0822019-07-17 13:54:30 -050024#include <xyz/openbmc_project/Common/File/error.hpp>
25
Patrick Williams2544b412022-10-04 08:41:06 -050026#include <fstream>
27
Matt Spinler89fa0822019-07-17 13:54:30 -050028namespace openpower
29{
30namespace pels
31{
32
33namespace fs = std::filesystem;
Matt Spinler89fa0822019-07-17 13:54:30 -050034namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
35
Matt Spinler7e727a32020-07-07 15:00:17 -050036constexpr size_t warningPercentage = 95;
37
Matt Spinlerdd325c32020-07-07 11:01:54 -050038/**
39 * @brief Returns the amount of space the file uses on disk.
40 *
41 * This is different than just the regular size of the file.
42 *
43 * @param[in] file - The file to get the size of
44 *
45 * @return size_t The disk space the file uses
46 */
47size_t getFileDiskSize(const std::filesystem::path& file)
48{
49 constexpr size_t statBlockSize = 512;
50 struct stat statData;
51 auto rc = stat(file.c_str(), &statData);
52 if (rc != 0)
53 {
54 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -050055 lg2::error("Call to stat() failed on {FILE} with errno {ERRNO}", "FILE",
56 file.native(), "ERRNO", e);
Matt Spinlerdd325c32020-07-07 11:01:54 -050057 abort();
58 }
59
60 return statData.st_blocks * statBlockSize;
61}
62
Matt Spinler8d5f3a22020-07-07 10:30:33 -050063Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
64 size_t maxNumPELs) :
Patrick Williams075c7922024-08-16 15:19:49 -040065 _logPath(basePath / "logs"), _maxRepoSize(repoSize),
66 _maxNumPELs(maxNumPELs), _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 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500111 lg2::error(
112 "Failed to save PEL after updating host state, PEL ID = {ID}",
113 "ID", lg2::hex, pel.id());
Matt Spinlera3c12a42019-11-21 13:25:32 -0600114 }
115 }
116
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500117 PELAttributes attributes{
118 dirEntry.path(),
119 getFileDiskSize(dirEntry.path()),
120 pel.privateHeader().creatorID(),
121 pel.userHeader().subsystem(),
122 pel.userHeader().severity(),
123 pel.userHeader().actionFlags(),
124 pel.hostTransmissionState(),
125 pel.hmcTransmissionState(),
126 pel.plid(),
127 pel.getDeconfigFlag(),
128 pel.getGuardFlag(),
129 getMillisecondsSinceEpoch(
130 pel.privateHeader().createTimestamp())};
Matt Spinler0ff00482019-11-06 16:19:46 -0600131
Matt Spinler475e5742019-07-18 16:09:49 -0500132 using pelID = LogID::Pel;
133 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600134 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500135 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600136 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500137
138 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500139 }
140 else
141 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500142 lg2::error(
143 "Found invalid PEL file {FILE} while restoring. Removing.",
144 "FILE", dirEntry.path());
Matt Spinler475e5742019-07-18 16:09:49 -0500145 fs::remove(dirEntry.path());
146 }
147 }
Patrick Williams66491c62021-10-06 12:23:37 -0500148 catch (const std::exception& e)
Matt Spinler475e5742019-07-18 16:09:49 -0500149 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500150 lg2::error("Hit exception while restoring PEL file {FILE}: {ERROR}",
151 "FILE", dirEntry.path(), "ERROR", e);
Matt Spinler475e5742019-07-18 16:09:49 -0500152 }
153 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500154
155 // Get size of archive folder
156 for (auto& dirEntry : fs::directory_iterator(_archivePath))
157 {
158 _archiveSize += getFileDiskSize(dirEntry);
159 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500160}
161
162std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
163{
164 char name[50];
165 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
166 time.yearLSB, time.month, time.day, time.hour, time.minutes,
167 time.seconds, time.hundredths, pelID);
168 return std::string{name};
169}
170
171void Repository::add(std::unique_ptr<PEL>& pel)
172{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600173 pel->setHostTransmissionState(TransmissionState::newPEL);
174 pel->setHMCTransmissionState(TransmissionState::newPEL);
175
Matt Spinler89fa0822019-07-17 13:54:30 -0500176 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600177
178 write(*(pel.get()), path);
179
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500180 PELAttributes attributes{
181 path,
182 getFileDiskSize(path),
183 pel->privateHeader().creatorID(),
184 pel->userHeader().subsystem(),
185 pel->userHeader().severity(),
186 pel->userHeader().actionFlags(),
187 pel->hostTransmissionState(),
188 pel->hmcTransmissionState(),
189 pel->plid(),
190 pel->getDeconfigFlag(),
191 pel->getGuardFlag(),
192 getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600193
194 using pelID = LogID::Pel;
195 using obmcID = LogID::Obmc;
196 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
197 attributes);
198
Matt Spinler44893cc2020-08-26 11:34:17 -0500199 _lastPelID = pel->id();
200
Matt Spinlerb188f782020-07-07 11:18:12 -0500201 updateRepoStats(attributes, true);
202
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600203 processAddCallbacks(*pel);
204}
205
206void Repository::write(const PEL& pel, const fs::path& path)
207{
Matt Spinler89fa0822019-07-17 13:54:30 -0500208 std::ofstream file{path, std::ios::binary};
209
210 if (!file.good())
211 {
212 // If this fails, the filesystem is probably full so it isn't like
213 // we could successfully create yet another error log here.
214 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500215 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500216 lg2::error(
217 "Unable to open PEL file {FILE} for writing, errno = {ERRNO}",
218 "FILE", path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500219 throw file_error::Open();
220 }
221
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600222 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500223 file.write(reinterpret_cast<const char*>(data.data()), data.size());
224
225 if (file.fail())
226 {
227 // Same note as above about not being able to create an error log
228 // for this case even if we wanted.
229 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500230 file.close();
231 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500232 lg2::error("Unable to write PEL file {FILE}, errno = {ERRNO}", "FILE",
233 path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500234 throw file_error::Write();
235 }
Matt Spinler475e5742019-07-18 16:09:49 -0500236}
237
Matt Spinler52602e32020-07-15 12:37:28 -0500238std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500239{
240 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500241 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500242 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500243 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600244 }
Matt Spinler52602e32020-07-15 12:37:28 -0500245
Patrick Williamsff6b5982021-04-22 09:04:17 -0500246 LogID actualID = pel->first;
247 updateRepoStats(pel->second, false);
248
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500249 lg2::debug(
250 "Removing PEL from repository, PEL ID = {PEL_ID}, BMC log ID = {BMC_ID}",
251 "PEL_ID", lg2::hex, actualID.pelID.id, "BMC_ID", actualID.obmcID.id);
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500252
253 if (fs::exists(pel->second.path))
254 {
255 // Check for existense of new archive folder
256 if (!fs::exists(_archivePath))
257 {
258 fs::create_directories(_archivePath);
259 }
260
261 // Move log file to archive folder
262 auto fileName = _archivePath / pel->second.path.filename();
263 fs::rename(pel->second.path, fileName);
264
265 // Update size of file
266 _archiveSize += getFileDiskSize(fileName);
267 }
268
Patrick Williamsff6b5982021-04-22 09:04:17 -0500269 _pelAttributes.erase(pel);
270
271 processDeleteCallbacks(actualID.pelID.id);
272
Matt Spinler52602e32020-07-15 12:37:28 -0500273 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500274}
275
Matt Spinler2813f362019-07-19 12:45:28 -0500276std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
277{
278 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600279 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500280 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600281 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500282 if (!file.good())
283 {
284 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500285 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
286 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler2813f362019-07-19 12:45:28 -0500287 throw file_error::Open();
288 }
289
290 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
291 std::istreambuf_iterator<char>()};
292 return data;
293 }
294
295 return std::nullopt;
296}
297
Matt Spinler6d512242019-12-09 13:44:17 -0600298std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
299{
300 auto pel = findPEL(id);
301 if (pel != _pelAttributes.end())
302 {
Matt Spinler32a6df62023-01-12 16:30:40 -0600303 int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK);
304 if (fd == -1)
Matt Spinler6d512242019-12-09 13:44:17 -0600305 {
306 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500307 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
308 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler6d512242019-12-09 13:44:17 -0600309 throw file_error::Open();
310 }
311
312 // Must leave the file open here. It will be closed by sdbusplus
313 // when it sends it back over D-Bus.
Matt Spinler32a6df62023-01-12 16:30:40 -0600314 return fd;
Matt Spinler6d512242019-12-09 13:44:17 -0600315 }
316 return std::nullopt;
317}
318
Matt Spinler1ea78802019-11-01 13:04:59 -0500319void Repository::for_each(ForEachFunc func) const
320{
Matt Spinler0ff00482019-11-06 16:19:46 -0600321 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500322 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600323 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500324
325 if (!file.good())
326 {
327 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500328 lg2::error(
329 "Repository::for_each: Unable to open PEL file {FILE}, errno = {ERRNO}",
330 "FILE", attributes.path, "ERRNO", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500331 continue;
332 }
333
334 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
335 std::istreambuf_iterator<char>()};
336 file.close();
337
338 PEL pel{data};
339
340 try
341 {
342 if (func(pel))
343 {
344 break;
345 }
346 }
Patrick Williams66491c62021-10-06 12:23:37 -0500347 catch (const std::exception& e)
Matt Spinler1ea78802019-11-01 13:04:59 -0500348 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500349 lg2::error("Repository::for_each function exception: {ERROR}",
350 "ERROR", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500351 }
352 }
353}
354
Matt Spinler421f6532019-11-06 15:40:45 -0600355void Repository::processAddCallbacks(const PEL& pel) const
356{
357 for (auto& [name, func] : _addSubscriptions)
358 {
359 try
360 {
361 func(pel);
362 }
Patrick Williams66491c62021-10-06 12:23:37 -0500363 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600364 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500365 lg2::error(
366 "PEL Repository add callback exception. Name = {NAME}, Error = {ERROR}",
367 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600368 }
369 }
370}
371
372void Repository::processDeleteCallbacks(uint32_t id) const
373{
374 for (auto& [name, func] : _deleteSubscriptions)
375 {
376 try
377 {
378 func(id);
379 }
Patrick Williams66491c62021-10-06 12:23:37 -0500380 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600381 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500382 lg2::error(
383 "PEL Repository delete callback exception. Name = {NAME}, Error = {ERROR}",
384 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600385 }
386 }
387}
388
Matt Spinler0ff00482019-11-06 16:19:46 -0600389std::optional<std::reference_wrapper<const Repository::PELAttributes>>
390 Repository::getPELAttributes(const LogID& id) const
391{
392 auto pel = findPEL(id);
393 if (pel != _pelAttributes.end())
394 {
395 return pel->second;
396 }
397
398 return std::nullopt;
399}
400
Matt Spinler29d18c12019-11-21 13:31:27 -0600401void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
402{
403 LogID id{LogID::Pel{pelID}};
404 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
405 [&id](const auto& a) { return a.first == id; });
406
407 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
408 {
409 PELUpdateFunc func = [state](PEL& pel) {
410 pel.setHostTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500411 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600412 };
413
414 try
415 {
416 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600417 }
Patrick Williams66491c62021-10-06 12:23:37 -0500418 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600419 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500420 lg2::error(
421 "Unable to update PEL host transmission state. Path = {PATH}, Error = {ERROR}",
422 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600423 }
424 }
425}
426
427void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
428{
429 LogID id{LogID::Pel{pelID}};
430 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
431 [&id](const auto& a) { return a.first == id; });
432
433 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
434 {
435 PELUpdateFunc func = [state](PEL& pel) {
436 pel.setHMCTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500437 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600438 };
439
440 try
441 {
442 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600443 }
Patrick Williams66491c62021-10-06 12:23:37 -0500444 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600445 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500446 lg2::error(
447 "Unable to update PEL HMC transmission state. Path = {PATH}, Error = {ERROR}",
448 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600449 }
450 }
451}
452
Matt Spinler1cb59f72023-07-20 09:49:50 -0500453bool Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
Matt Spinler29d18c12019-11-21 13:31:27 -0600454{
455 std::ifstream file{path};
456 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
457 std::istreambuf_iterator<char>()};
458 file.close();
459
460 PEL pel{data};
461
462 if (pel.valid())
463 {
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500464 if (updateFunc(pel))
465 {
466 // Three attribute fields can change post creation from
467 // an updatePEL call:
468 // - hmcTransmissionState - When HMC acks a PEL
469 // - hostTransmissionState - When host acks a PEL
470 // - deconfig flag - Can be cleared for PELs that call out
471 // hotplugged FRUs.
472 // Make sure they're up to date.
473 LogID id{LogID::Pel(pel.id())};
474 auto attr =
475 std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
476 [&id](const auto& a) { return a.first == id; });
477 if (attr != _pelAttributes.end())
478 {
479 attr->second.hmcState = pel.hmcTransmissionState();
480 attr->second.hostState = pel.hostTransmissionState();
481 attr->second.deconfig = pel.getDeconfigFlag();
482 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600483
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500484 write(pel, path);
Matt Spinler1cb59f72023-07-20 09:49:50 -0500485 return true;
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500486 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600487 }
488 else
489 {
490 throw std::runtime_error(
491 "Unable to read a valid PEL when trying to update it");
492 }
Matt Spinler1cb59f72023-07-20 09:49:50 -0500493 return false;
Matt Spinler29d18c12019-11-21 13:31:27 -0600494}
495
Matt Spinlerb188f782020-07-07 11:18:12 -0500496bool Repository::isServiceableSev(const PELAttributes& pel)
497{
498 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
Patrick Williams075c7922024-08-16 15:19:49 -0400499 auto sevPVEntry =
500 pel_values::findByValue(pel.severity, pel_values::severityValues);
Matt Spinlerb188f782020-07-07 11:18:12 -0500501 std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
502
503 bool check1 = (sevType == SeverityType::predictive) ||
504 (sevType == SeverityType::unrecoverable) ||
505 (sevType == SeverityType::critical);
506
507 bool check2 = ((sevType == SeverityType::recovered) ||
508 (sevName == "symptom_recovered")) &&
509 !pel.actionFlags.test(hiddenFlagBit);
510
511 bool check3 = (sevName == "symptom_predictive") ||
512 (sevName == "symptom_unrecoverable") ||
513 (sevName == "symptom_critical");
514
515 return check1 || check2 || check3;
516}
517
518void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
519{
520 auto isServiceable = Repository::isServiceableSev(pel);
521 auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
522
523 auto adjustSize = [pelAdded, &pel](auto& runningSize) {
524 if (pelAdded)
525 {
526 runningSize += pel.sizeOnDisk;
527 }
528 else
529 {
530 runningSize = std::max(static_cast<int64_t>(runningSize) -
531 static_cast<int64_t>(pel.sizeOnDisk),
532 static_cast<int64_t>(0));
533 }
534 };
535
536 adjustSize(_sizes.total);
537
538 if (bmcPEL)
539 {
540 adjustSize(_sizes.bmc);
541 if (isServiceable)
542 {
543 adjustSize(_sizes.bmcServiceable);
544 }
545 else
546 {
547 adjustSize(_sizes.bmcInfo);
548 }
549 }
550 else
551 {
552 adjustSize(_sizes.nonBMC);
553 if (isServiceable)
554 {
555 adjustSize(_sizes.nonBMCServiceable);
556 }
557 else
558 {
559 adjustSize(_sizes.nonBMCInfo);
560 }
561 }
562}
563
Sumit Kumarc2966922021-07-21 10:14:03 -0500564bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500565{
Sumit Kumarc2966922021-07-21 10:14:03 -0500566 std::error_code ec;
567
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500568 if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
569 ((_maxRepoSize * warningPercentage) / 100)))
570 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500571 lg2::info(
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500572 "Repository::sizeWarning function:Deleting the files in archive");
573
Sumit Kumarc2966922021-07-21 10:14:03 -0500574 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500575 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500576 fs::remove(dirEntry.path(), ec);
577 if (ec)
578 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500579 lg2::info("Repository::sizeWarning: Could not delete "
580 "file {FILE} in PEL archive",
581 "FILE", dirEntry.path());
Sumit Kumarc2966922021-07-21 10:14:03 -0500582 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500583 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500584
585 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500586 }
587
Matt Spinler7e727a32020-07-07 15:00:17 -0500588 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
589 (_pelAttributes.size() > _maxNumPELs);
590}
591
Patrick Williams25291152025-02-01 08:21:42 -0500592std::vector<Repository::AttributesReference> Repository::getAllPELAttributes(
593 SortOrder order) const
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500594{
595 std::vector<Repository::AttributesReference> attributes;
596
Patrick Williams075c7922024-08-16 15:19:49 -0400597 std::for_each(_pelAttributes.begin(), _pelAttributes.end(),
598 [&attributes](auto& pelEntry) {
599 attributes.push_back(pelEntry);
600 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500601
602 std::sort(attributes.begin(), attributes.end(),
603 [order](const auto& left, const auto& right) {
Patrick Williams075c7922024-08-16 15:19:49 -0400604 if (order == SortOrder::ascending)
605 {
606 return left.get().second.path < right.get().second.path;
607 }
608 return left.get().second.path > right.get().second.path;
609 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500610
611 return attributes;
612}
613
Patrick Williams25291152025-02-01 08:21:42 -0500614std::vector<uint32_t> Repository::prune(
615 const std::vector<uint32_t>& idsWithHwIsoEntry)
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500616{
617 std::vector<uint32_t> obmcLogIDs;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500618 lg2::info("Pruning PEL repository that takes up {TOTAL} bytes and has "
619 "{NUM_PELS} PELs",
620 "TOTAL", _sizes.total, "NUM_PELS", _pelAttributes.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500621
622 // Set up the 5 functions to check if the PEL category
623 // is still over its limits.
624
625 // BMC informational PELs should only take up 15%
626 IsOverLimitFunc overBMCInfoLimit = [this]() {
627 return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
628 };
629
630 // BMC non informational PELs should only take up 30%
631 IsOverLimitFunc overBMCNonInfoLimit = [this]() {
632 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
633 };
634
635 // Non BMC informational PELs should only take up 15%
636 IsOverLimitFunc overNonBMCInfoLimit = [this]() {
637 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
638 };
639
640 // Non BMC non informational PELs should only take up 15%
641 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
642 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
643 };
644
645 // Bring the total number of PELs down to 80% of the max
646 IsOverLimitFunc tooManyPELsLimit = [this]() {
647 return _pelAttributes.size() > _maxNumPELs * 80 / 100;
648 };
649
650 // Set up the functions to determine which category a PEL is in.
651 // TODO: Return false in these functions if a PEL caused a guard record.
652
653 // A BMC informational PEL
654 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
655 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
656 !Repository::isServiceableSev(pel);
657 };
658
659 // A BMC non informational PEL
660 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
661 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
662 Repository::isServiceableSev(pel);
663 };
664
665 // A non BMC informational PEL
666 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
667 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
668 !Repository::isServiceableSev(pel);
669 };
670
671 // A non BMC non informational PEL
672 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
673 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
674 Repository::isServiceableSev(pel);
675 };
676
677 // When counting PELs, count every PEL
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500678 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500679
680 // Check all 4 categories, which will result in at most 90%
681 // usage (15 + 30 + 15 + 30).
Sumit Kumar027bf282022-01-24 11:25:19 -0600682 removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs);
683 removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry,
684 obmcLogIDs);
685 removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry,
686 obmcLogIDs);
687 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry,
688 obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500689
690 // After the above pruning check if there are still too many PELs,
691 // which can happen depending on PEL sizes.
692 if (_pelAttributes.size() > _maxNumPELs)
693 {
Sumit Kumar027bf282022-01-24 11:25:19 -0600694 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500695 }
696
697 if (!obmcLogIDs.empty())
698 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500699 lg2::info("Number of PELs removed to save space: {NUM_PELS}",
700 "NUM_PELS", obmcLogIDs.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500701 }
702
703 return obmcLogIDs;
704}
705
Matt Spinler45796e82022-07-01 11:25:27 -0500706void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
707 const IsPELTypeFunc& isPELType,
Sumit Kumar027bf282022-01-24 11:25:19 -0600708 const std::vector<uint32_t>& idsWithHwIsoEntry,
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500709 std::vector<uint32_t>& removedBMCLogIDs)
710{
711 if (!isOverLimit())
712 {
713 return;
714 }
715
716 auto attributes = getAllPELAttributes(SortOrder::ascending);
717
718 // Make 4 passes on the PELs, stopping as soon as isOverLimit
719 // returns false.
720 // Pass 1: only delete HMC acked PELs
721 // Pass 2: only delete OS acked PELs
722 // Pass 3: only delete PHYP sent PELs
723 // Pass 4: delete all PELs
724 static const std::vector<std::function<bool(const PELAttributes& pel)>>
725 stateChecks{[](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400726 return pel.hmcState == TransmissionState::acked;
727 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500728
729 [](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400730 return pel.hostState == TransmissionState::acked;
731 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500732
Patrick Williams5fb575a2023-10-20 11:18:21 -0500733 [](const auto& pel) {
Patrick Williams075c7922024-08-16 15:19:49 -0400734 return pel.hostState == TransmissionState::sent;
735 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500736
Patrick Williams5fb575a2023-10-20 11:18:21 -0500737 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500738
739 for (const auto& stateCheck : stateChecks)
740 {
741 for (auto it = attributes.begin(); it != attributes.end();)
742 {
743 const auto& pel = it->get();
744 if (isPELType(pel.second) && stateCheck(pel.second))
745 {
746 auto removedID = pel.first.obmcID.id;
Sumit Kumar027bf282022-01-24 11:25:19 -0600747
748 auto idFound = std::find(idsWithHwIsoEntry.begin(),
749 idsWithHwIsoEntry.end(), removedID);
750 if (idFound != idsWithHwIsoEntry.end())
751 {
752 ++it;
753 continue;
754 }
755
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500756 remove(pel.first);
757
758 removedBMCLogIDs.push_back(removedID);
759
760 attributes.erase(it);
761
762 if (!isOverLimit())
763 {
764 break;
765 }
766 }
767 else
768 {
769 ++it;
770 }
771 }
772
773 if (!isOverLimit())
774 {
775 break;
776 }
777 }
778}
779
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500780void Repository::archivePEL(const PEL& pel)
781{
782 if (pel.valid())
783 {
784 auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime());
785
786 write(pel, path);
787
788 _archiveSize += getFileDiskSize(path);
789 }
790}
791
Matt Spinler89fa0822019-07-17 13:54:30 -0500792} // namespace pels
793} // namespace openpower