1 #pragma once 2 #include "bcd_time.hpp" 3 #include "paths.hpp" 4 #include "pel.hpp" 5 6 #include <algorithm> 7 #include <bitset> 8 #include <filesystem> 9 #include <map> 10 11 namespace openpower 12 { 13 namespace pels 14 { 15 16 /** 17 * @class Repository 18 * 19 * The class handles saving and retrieving PELs on the BMC. 20 */ 21 class Repository 22 { 23 public: 24 /** 25 * @brief Structure of commonly used PEL attributes. 26 */ 27 struct PELAttributes 28 { 29 std::filesystem::path path; 30 size_t sizeOnDisk; 31 uint8_t creator; 32 uint8_t subsystem; 33 uint8_t severity; 34 std::bitset<16> actionFlags; 35 TransmissionState hostState; 36 TransmissionState hmcState; 37 38 PELAttributes() = delete; 39 40 PELAttributes(const std::filesystem::path& p, size_t size, 41 uint8_t creator, uint8_t subsystem, uint8_t sev, 42 uint16_t flags, TransmissionState hostState, 43 TransmissionState hmcState) : 44 path(p), 45 sizeOnDisk(size), creator(creator), subsystem(subsystem), 46 severity(sev), actionFlags(flags), hostState(hostState), 47 hmcState(hmcState) 48 { 49 } 50 }; 51 52 /** 53 * @brief A structure that holds both the PEL and corresponding 54 * OpenBMC IDs. 55 * Used for correlating the IDs with their data files for quick 56 * lookup. To find a PEL based on just one of the IDs, just use 57 * the constructor that takes that ID. 58 */ 59 struct LogID 60 { 61 struct Pel 62 { 63 uint32_t id; 64 explicit Pel(uint32_t i) : id(i) 65 { 66 } 67 }; 68 struct Obmc 69 { 70 uint32_t id; 71 explicit Obmc(uint32_t i) : id(i) 72 { 73 } 74 }; 75 76 Pel pelID; 77 78 Obmc obmcID; 79 80 LogID(Pel pel, Obmc obmc) : pelID(pel), obmcID(obmc) 81 { 82 } 83 84 explicit LogID(Pel id) : pelID(id), obmcID(0) 85 { 86 } 87 88 explicit LogID(Obmc id) : pelID(0), obmcID(id) 89 { 90 } 91 92 LogID() = delete; 93 94 /** 95 * @brief A == operator that will match on either ID 96 * being equal if the other is zero, so that 97 * one can look up a PEL with just one of the IDs. 98 */ 99 bool operator==(const LogID& id) const 100 { 101 if (id.pelID.id != 0) 102 { 103 return id.pelID.id == pelID.id; 104 } 105 if (id.obmcID.id != 0) 106 { 107 return id.obmcID.id == obmcID.id; 108 } 109 return false; 110 } 111 112 bool operator<(const LogID& id) const 113 { 114 return pelID.id < id.pelID.id; 115 } 116 }; 117 118 using AttributesReference = 119 std::reference_wrapper<const std::pair<const LogID, PELAttributes>>; 120 121 /** 122 * @brief A structure for keeping a breakdown of the sizes of PELs 123 * of different types in the repository. 124 */ 125 struct SizeStats 126 { 127 uint64_t total; 128 uint64_t bmc; 129 uint64_t nonBMC; 130 uint64_t bmcServiceable; 131 uint64_t bmcInfo; 132 uint64_t nonBMCServiceable; 133 uint64_t nonBMCInfo; 134 135 SizeStats() : 136 total(0), bmc(0), nonBMC(0), bmcServiceable(0), bmcInfo(0), 137 nonBMCServiceable(0), nonBMCInfo(0) 138 { 139 } 140 }; 141 142 Repository() = delete; 143 ~Repository() = default; 144 Repository(const Repository&) = default; 145 Repository& operator=(const Repository&) = default; 146 Repository(Repository&&) = default; 147 Repository& operator=(Repository&&) = default; 148 149 /** 150 * @brief Constructor 151 * 152 * @param[in] basePath - the base filesystem path for the repository 153 */ 154 explicit Repository(const std::filesystem::path& basePath) : 155 Repository(basePath, getPELRepoSize(), getMaxNumPELs()) 156 { 157 } 158 159 /** 160 * @brief Constructor that takes the repository size 161 * 162 * @param[in] basePath - the base filesystem path for the repository 163 * @param[in] repoSize - The maximum amount of space to use for PELs, 164 * in bytes 165 * @param[in] maxNumPELs - The maximum number of PELs to allow 166 */ 167 Repository(const std::filesystem::path& basePath, size_t repoSize, 168 size_t maxNumPELs); 169 170 /** 171 * @brief Adds a PEL to the repository 172 * 173 * Throws File.Error.Open or File.Error.Write exceptions on failure 174 * 175 * @param[in] pel - the PEL to add 176 */ 177 void add(std::unique_ptr<PEL>& pel); 178 179 /** 180 * @brief Removes a PEL from the repository 181 * 182 * Note that the returned LogID is the fully filled in LogID, i.e. 183 * it has both the PEL and OpenBMC IDs, unlike the passed in LogID 184 * which can just have one or the other. 185 * 186 * @param[in] id - the ID (either the pel ID, OBMC ID, or both) to remove 187 * 188 * @return std::optional<LogID> - The LogID of the removed PEL 189 */ 190 std::optional<LogID> remove(const LogID& id); 191 192 /** 193 * @brief Generates the filename to use for the PEL ID and BCDTime. 194 * 195 * @param[in] pelID - the PEL ID 196 * @param[in] time - the BCD time 197 * 198 * @return string - A filename string of <BCD_time>_<pelID> 199 */ 200 static std::string getPELFilename(uint32_t pelID, const BCDTime& time); 201 202 /** 203 * @brief Returns true if the PEL with the specified ID is in the repo. 204 * 205 * @param[in] id - the ID (either the pel ID, OBMC ID, or both) 206 * @return bool - true if that PEL is present 207 */ 208 inline bool hasPEL(const LogID& id) 209 { 210 return findPEL(id) != _pelAttributes.end(); 211 } 212 213 /** 214 * @brief Returns the PEL data based on its ID. 215 * 216 * If the data can't be found for that ID, then the optional object 217 * will be empty. 218 * 219 * @param[in] id - the LogID to get the PEL for, which can be either a 220 * PEL ID or OpenBMC log ID. 221 * @return std::optional<std::vector<uint8_t>> - the PEL data 222 */ 223 std::optional<std::vector<uint8_t>> getPELData(const LogID& id); 224 225 /** 226 * @brief Get a file descriptor to the PEL data 227 * 228 * @param[in] id - The ID to get the FD for 229 * 230 * @return std::optional<sdbusplus::message::unix_fd> - 231 * The FD, or an empty optional object. 232 */ 233 std::optional<sdbusplus::message::unix_fd> getPELFD(const LogID& id); 234 235 using ForEachFunc = std::function<bool(const PEL&)>; 236 237 /** 238 * @brief Run a user defined function on every PEL in the repository. 239 * 240 * ForEachFunc takes a const PEL reference, and should return 241 * true to stop iterating and return out of for_each. 242 * 243 * For example, to save up to 100 IDs in the repo into a vector: 244 * 245 * std::vector<uint32_t> ids; 246 * ForEachFunc f = [&ids](const PEL& pel) { 247 * ids.push_back(pel.id()); 248 * return ids.size() == 100 ? true : false; 249 * }; 250 * 251 * @param[in] func - The function to run. 252 */ 253 void for_each(ForEachFunc func) const; 254 255 using AddCallback = std::function<void(const PEL&)>; 256 257 /** 258 * @brief Subscribe to PELs being added to the repository. 259 * 260 * Every time a PEL is added to the repository, the provided 261 * function will be called with the new PEL as the argument. 262 * 263 * The function must be of type void(const PEL&). 264 * 265 * @param[in] name - The subscription name 266 * @param[in] func - The callback function 267 */ 268 void subscribeToAdds(const std::string& name, AddCallback func) 269 { 270 _addSubscriptions.emplace(name, func); 271 } 272 273 /** 274 * @brief Unsubscribe from new PELs. 275 * 276 * @param[in] name - The subscription name 277 */ 278 void unsubscribeFromAdds(const std::string& name) 279 { 280 _addSubscriptions.erase(name); 281 } 282 283 using DeleteCallback = std::function<void(uint32_t)>; 284 285 /** 286 * @brief Subscribe to PELs being deleted from the repository. 287 * 288 * Every time a PEL is deleted from the repository, the provided 289 * function will be called with the PEL ID as the argument. 290 * 291 * The function must be of type void(const uint32_t). 292 * 293 * @param[in] name - The subscription name 294 * @param[in] func - The callback function 295 */ 296 void subscribeToDeletes(const std::string& name, DeleteCallback func) 297 { 298 _deleteSubscriptions.emplace(name, func); 299 } 300 301 /** 302 * @brief Unsubscribe from deleted PELs. 303 * 304 * @param[in] name - The subscription name 305 */ 306 void unsubscribeFromDeletes(const std::string& name) 307 { 308 _deleteSubscriptions.erase(name); 309 } 310 311 /** 312 * @brief Get the PEL attributes for a PEL 313 * 314 * @param[in] id - The ID to find the attributes for 315 * 316 * @return The attributes or an empty optional if not found 317 */ 318 std::optional<std::reference_wrapper<const PELAttributes>> 319 getPELAttributes(const LogID& id) const; 320 321 /** 322 * @brief Sets the host transmission state on a PEL file 323 * 324 * Writes the host transmission state field in the User Header 325 * section in the PEL data specified by the ID. 326 * 327 * @param[in] pelID - The PEL ID 328 * @param[in] state - The state to write 329 */ 330 void setPELHostTransState(uint32_t pelID, TransmissionState state); 331 332 /** 333 * @brief Sets the HMC transmission state on a PEL file 334 * 335 * Writes the HMC transmission state field in the User Header 336 * section in the PEL data specified by the ID. 337 * 338 * @param[in] pelID - The PEL ID 339 * @param[in] state - The state to write 340 */ 341 void setPELHMCTransState(uint32_t pelID, TransmissionState state); 342 343 /** 344 * @brief Returns the size stats structure 345 * 346 * @return const SizeStats& - The stats structure 347 */ 348 const SizeStats& getSizeStats() const 349 { 350 return _sizes; 351 } 352 353 /** 354 * @brief Says if the PEL is considered serviceable (not just 355 * informational) as determined by its severity. 356 * 357 * @param[in] pel - The PELAttributes entry for the PEL 358 * @return bool - If serviceable or not 359 */ 360 static bool isServiceableSev(const PELAttributes& pel); 361 362 /** 363 * @brief Returns true if the total amount of disk space occupied 364 * by the PELs in the repo is over 95% of the maximum 365 * size, or if there are over the maximum number of 366 * PELs allowed. 367 * 368 * @return bool - true if repo is > 95% full or too many PELs 369 */ 370 bool sizeWarning(); 371 372 /** 373 * @brief Deletes PELs to bring the repository size down 374 * to at most 90% full by placing PELs into 4 different 375 * catogories and then removing PELs until those catogories 376 * only take up certain percentages of the allowed space. 377 * 378 * This does not delete the corresponding OpenBMC event logs, which 379 * is why those IDs are returned, so they can be deleted later. 380 * 381 * The categories and their rules are: 382 * 1) Informational BMC PELs cannot take up more than 15% of 383 * the allocated space. 384 * 2) Non-informational BMC PELs cannot take up more than 30% 385 * of the allocated space. 386 * 3) Informational non-BMC PELs cannot take up more than 15% of 387 * the allocated space. 388 * 4) Non-informational non-BMC PELs cannot take up more than 30% 389 * of the allocated space. 390 * 391 * While removing PELs in a category, 4 passes will be made, with 392 * PELs being removed oldest first during each pass. 393 * 394 * Pass 1: only delete HMC acked PELs 395 * Pass 2: only delete OS acked PELs 396 * Pass 3: only delete PHYP sent PELs 397 * Pass 4: delete all PELs 398 * 399 * @param[in] ids - The OpenBMC event log Ids with hardware isolation entry. 400 * 401 * @return std::vector<uint32_t> - The OpenBMC event log IDs of 402 * the PELs that were deleted. 403 */ 404 std::vector<uint32_t> prune(const std::vector<uint32_t>& idsWithHwIsoEntry); 405 406 /** 407 * @brief Returns the path to the directory where the PEL 408 * files are stored. 409 * 410 * @return std::filesystem::path - The directory path 411 */ 412 const std::filesystem::path& repoPath() const 413 { 414 return _logPath; 415 } 416 417 /** 418 * @brief Returns the ID of the most recently added PEL. 419 * 420 * @return uint32_t - The PEL ID 421 */ 422 uint32_t lastPelID() const 423 { 424 return _lastPelID; 425 } 426 427 /** 428 * @brief Get the LogID based on the given ObmcLogId or PelId. 429 * 430 * @param[in] id - The ID to find the LogID. 431 * 432 * @return The LogID or an empty optional if not found. 433 * 434 * @note The returned LogID is the fully filled in LogID, i.e. 435 * it has both the PEL and OpenBMC Log IDs, unlike the passed in LogID 436 * which can just have one or the other. 437 */ 438 std::optional<LogID> getLogID(const LogID& id) const 439 { 440 if (auto logID = findPEL(id); logID != _pelAttributes.end()) 441 { 442 return logID->first; 443 } 444 return std::nullopt; 445 } 446 447 /** 448 * @brief Save the PEL to archive folder 449 * 450 * @param[in] pel - The PEL data 451 */ 452 void archivePEL(const PEL& pel); 453 454 private: 455 using PELUpdateFunc = std::function<void(PEL&)>; 456 457 /** 458 * @brief Lets a function modify a PEL and saves the results 459 * 460 * Runs updateFunc (a void(PEL&) function) on the PEL data 461 * on the file specified, and writes the results back to the file. 462 * 463 * @param[in] path - The file path to use 464 * @param[in] updateFunc - The function to run to update the PEL. 465 */ 466 void updatePEL(const std::filesystem::path& path, PELUpdateFunc updateFunc); 467 468 /** 469 * @brief Finds an entry in the _pelAttributes map. 470 * 471 * @param[in] id - the ID (either the pel ID, OBMC ID, or both) 472 * 473 * @return an iterator to the entry 474 */ 475 std::map<LogID, PELAttributes>::const_iterator 476 findPEL(const LogID& id) const 477 { 478 return std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 479 [&id](const auto& a) { return a.first == id; }); 480 } 481 482 /** 483 * @brief Call any subscribed functions for new PELs 484 * 485 * @param[in] pel - The new PEL 486 */ 487 void processAddCallbacks(const PEL& pel) const; 488 489 /** 490 * @brief Call any subscribed functions for deleted PELs 491 * 492 * @param[in] id - The ID of the deleted PEL 493 */ 494 void processDeleteCallbacks(uint32_t id) const; 495 496 /** 497 * @brief Restores the _pelAttributes map on startup based on the existing 498 * PEL data files. 499 */ 500 void restore(); 501 502 /** 503 * @brief Stores a PEL object in the filesystem. 504 * 505 * @param[in] pel - The PEL to write 506 * @param[in] path - The file to write to 507 * 508 * Throws exceptions on failures. 509 */ 510 void write(const PEL& pel, const std::filesystem::path& path); 511 512 /** 513 * @brief Updates the repository statistics after a PEL is 514 * added or removed. 515 * 516 * @param[in] pel - The PELAttributes entry for the PEL 517 * @param[in] pelAdded - true if the PEL was added, false if removed 518 */ 519 void updateRepoStats(const PELAttributes& pel, bool pelAdded); 520 521 enum class SortOrder 522 { 523 ascending, 524 descending 525 }; 526 527 /** 528 * @brief Returns a vector of all the _pelAttributes entries sorted 529 * as specified 530 * 531 * @param[in] order - If the PELs should be returned in ascending 532 * (oldest first) or descending order. 533 * 534 * @return std::vector<AttributesReference> - The sorted vector of 535 * references to the pair<LogID, PELAttributes> entries of 536 * _pelAttributes. 537 */ 538 std::vector<AttributesReference> getAllPELAttributes(SortOrder order) const; 539 540 using IsOverLimitFunc = std::function<bool()>; 541 using IsPELTypeFunc = std::function<bool(const PELAttributes&)>; 542 543 /** 544 * @brief Makes 4 passes on the PELs that meet the IsPELTypeFunc 545 * criteria removing PELs until IsOverLimitFunc returns false. 546 * 547 * Pass 1: only delete HMC acked PELs 548 * Pass 2: only delete Os acked PELs 549 * Pass 3: only delete PHYP sent PELs 550 * Pass 4: delete all PELs 551 * 552 * @param[in] isOverLimit - The bool(void) function that should 553 * return true if PELs still need to be 554 * removed. 555 * @param[in] isPELType - The bool(const PELAttributes&) function 556 * used to select the PELs to operate on. 557 * @param[in] ids - The OpenBMC event log Ids with hardware isolation 558 * entry. 559 * 560 * @param[out] removedBMCLogIDs - The OpenBMC event log IDs of the 561 * removed PELs. 562 */ 563 void removePELs(const IsOverLimitFunc& isOverLimit, 564 const IsPELTypeFunc& isPELType, 565 const std::vector<uint32_t>& idsWithHwIsoEntry, 566 std::vector<uint32_t>& removedBMCLogIDs); 567 /** 568 * @brief The filesystem path to the PEL logs. 569 */ 570 const std::filesystem::path _logPath; 571 572 /** 573 * @brief A map of the PEL/OBMC IDs to PEL attributes. 574 */ 575 std::map<LogID, PELAttributes> _pelAttributes; 576 577 /** 578 * @brief Subcriptions for new PELs. 579 */ 580 std::map<std::string, AddCallback> _addSubscriptions; 581 582 /** 583 * @brief Subscriptions for deleted PELs. 584 */ 585 std::map<std::string, DeleteCallback> _deleteSubscriptions; 586 587 /** 588 * @brief The maximum amount of space that the PELs in the 589 * repository can occupy. 590 */ 591 const uint64_t _maxRepoSize; 592 593 /** 594 * @brief The maximum number of PELs to allow in the repo 595 * before pruning. 596 */ 597 const size_t _maxNumPELs; 598 599 /** 600 * @brief Statistics on the sizes of the stored PELs. 601 */ 602 SizeStats _sizes; 603 604 /** 605 * @brief The ID of the most recently added PEL. 606 */ 607 uint32_t _lastPelID = 0; 608 609 /** 610 * @brief The filesystem path to the archive PEL logs. 611 */ 612 const std::filesystem::path _archivePath; 613 614 /** 615 * @brief The size of archive folder. 616 */ 617 uint64_t _archiveSize = 0; 618 }; 619 620 } // namespace pels 621 } // namespace openpower 622