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 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 if (_addSubscriptions.find(name) == _addSubscriptions.end()) 271 { 272 _addSubscriptions.emplace(name, func); 273 } 274 } 275 276 /** 277 * @brief Unsubscribe from new PELs. 278 * 279 * @param[in] name - The subscription name 280 */ 281 void unsubscribeFromAdds(const std::string& name) 282 { 283 _addSubscriptions.erase(name); 284 } 285 286 using DeleteCallback = std::function<void(uint32_t)>; 287 288 /** 289 * @brief Subscribe to PELs being deleted from the repository. 290 * 291 * Every time a PEL is deleted from the repository, the provided 292 * function will be called with the PEL ID as the argument. 293 * 294 * The function must be of type void(const uint32_t). 295 * 296 * @param[in] name - The subscription name 297 * @param[in] func - The callback function 298 */ 299 void subscribeToDeletes(const std::string& name, DeleteCallback func) 300 { 301 if (_deleteSubscriptions.find(name) == _deleteSubscriptions.end()) 302 { 303 _deleteSubscriptions.emplace(name, func); 304 } 305 } 306 307 /** 308 * @brief Unsubscribe from deleted PELs. 309 * 310 * @param[in] name - The subscription name 311 */ 312 void unsubscribeFromDeletes(const std::string& name) 313 { 314 _deleteSubscriptions.erase(name); 315 } 316 317 /** 318 * @brief Get the PEL attributes for a PEL 319 * 320 * @param[in] id - The ID to find the attributes for 321 * 322 * @return The attributes or an empty optional if not found 323 */ 324 std::optional<std::reference_wrapper<const PELAttributes>> 325 getPELAttributes(const LogID& id) const; 326 327 /** 328 * @brief Sets the host transmission state on a PEL file 329 * 330 * Writes the host transmission state field in the User Header 331 * section in the PEL data specified by the ID. 332 * 333 * @param[in] pelID - The PEL ID 334 * @param[in] state - The state to write 335 */ 336 void setPELHostTransState(uint32_t pelID, TransmissionState state); 337 338 /** 339 * @brief Sets the HMC transmission state on a PEL file 340 * 341 * Writes the HMC transmission state field in the User Header 342 * section in the PEL data specified by the ID. 343 * 344 * @param[in] pelID - The PEL ID 345 * @param[in] state - The state to write 346 */ 347 void setPELHMCTransState(uint32_t pelID, TransmissionState state); 348 349 /** 350 * @brief Returns the size stats structure 351 * 352 * @return const SizeStats& - The stats structure 353 */ 354 const SizeStats& getSizeStats() const 355 { 356 return _sizes; 357 } 358 359 /** 360 * @brief Says if the PEL is considered serviceable (not just 361 * informational) as determined by its severity. 362 * 363 * @param[in] pel - The PELAttributes entry for the PEL 364 * @return bool - If serviceable or not 365 */ 366 static bool isServiceableSev(const PELAttributes& pel); 367 368 /** 369 * @brief Returns true if the total amount of disk space occupied 370 * by the PELs in the repo is over 95% of the maximum 371 * size, or if there are over the maximum number of 372 * PELs allowed. 373 * 374 * @return bool - true if repo is > 95% full or too many PELs 375 */ 376 bool sizeWarning(); 377 378 /** 379 * @brief Deletes PELs to bring the repository size down 380 * to at most 90% full by placing PELs into 4 different 381 * catogories and then removing PELs until those catogories 382 * only take up certain percentages of the allowed space. 383 * 384 * This does not delete the corresponding OpenBMC event logs, which 385 * is why those IDs are returned, so they can be deleted later. 386 * 387 * The categories and their rules are: 388 * 1) Informational BMC PELs cannot take up more than 15% of 389 * the allocated space. 390 * 2) Non-informational BMC PELs cannot take up more than 30% 391 * of the allocated space. 392 * 3) Informational non-BMC PELs cannot take up more than 15% of 393 * the allocated space. 394 * 4) Non-informational non-BMC PELs cannot take up more than 30% 395 * of the allocated space. 396 * 397 * While removing PELs in a category, 4 passes will be made, with 398 * PELs being removed oldest first during each pass. 399 * 400 * Pass 1: only delete HMC acked PELs 401 * Pass 2: only delete OS acked PELs 402 * Pass 3: only delete PHYP sent PELs 403 * Pass 4: delete all PELs 404 * 405 * @return std::vector<uint32_t> - The OpenBMC event log IDs of 406 * the PELs that were deleted. 407 */ 408 std::vector<uint32_t> prune(); 409 410 /** 411 * @brief Returns the path to the directory where the PEL 412 * files are stored. 413 * 414 * @return std::filesystem::path - The directory path 415 */ 416 const std::filesystem::path& repoPath() const 417 { 418 return _logPath; 419 } 420 421 /** 422 * @brief Returns the ID of the most recently added PEL. 423 * 424 * @return uint32_t - The PEL ID 425 */ 426 uint32_t lastPelID() const 427 { 428 return _lastPelID; 429 } 430 431 /** 432 * @brief Get the LogID based on the given ObmcLogId or PelId. 433 * 434 * @param[in] id - The ID to find the LogID. 435 * 436 * @return The LogID or an empty optional if not found. 437 * 438 * @note The returned LogID is the fully filled in LogID, i.e. 439 * it has both the PEL and OpenBMC Log IDs, unlike the passed in LogID 440 * which can just have one or the other. 441 */ 442 std::optional<LogID> getLogID(const LogID& id) const 443 { 444 if (auto logID = findPEL(id); logID != _pelAttributes.end()) 445 { 446 return logID->first; 447 } 448 return std::nullopt; 449 } 450 451 /** 452 * @brief Save the PEL to archive folder 453 * 454 * @param[in] pel - The PEL data 455 */ 456 void archivePEL(const PEL& pel); 457 458 private: 459 using PELUpdateFunc = std::function<void(PEL&)>; 460 461 /** 462 * @brief Lets a function modify a PEL and saves the results 463 * 464 * Runs updateFunc (a void(PEL&) function) on the PEL data 465 * on the file specified, and writes the results back to the file. 466 * 467 * @param[in] path - The file path to use 468 * @param[in] updateFunc - The function to run to update the PEL. 469 */ 470 void updatePEL(const std::filesystem::path& path, PELUpdateFunc updateFunc); 471 472 /** 473 * @brief Finds an entry in the _pelAttributes map. 474 * 475 * @param[in] id - the ID (either the pel ID, OBMC ID, or both) 476 * 477 * @return an iterator to the entry 478 */ 479 std::map<LogID, PELAttributes>::const_iterator 480 findPEL(const LogID& id) const 481 { 482 return std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 483 [&id](const auto& a) { return a.first == id; }); 484 } 485 486 /** 487 * @brief Call any subscribed functions for new PELs 488 * 489 * @param[in] pel - The new PEL 490 */ 491 void processAddCallbacks(const PEL& pel) const; 492 493 /** 494 * @brief Call any subscribed functions for deleted PELs 495 * 496 * @param[in] id - The ID of the deleted PEL 497 */ 498 void processDeleteCallbacks(uint32_t id) const; 499 500 /** 501 * @brief Restores the _pelAttributes map on startup based on the existing 502 * PEL data files. 503 */ 504 void restore(); 505 506 /** 507 * @brief Stores a PEL object in the filesystem. 508 * 509 * @param[in] pel - The PEL to write 510 * @param[in] path - The file to write to 511 * 512 * Throws exceptions on failures. 513 */ 514 void write(const PEL& pel, const std::filesystem::path& path); 515 516 /** 517 * @brief Updates the repository statistics after a PEL is 518 * added or removed. 519 * 520 * @param[in] pel - The PELAttributes entry for the PEL 521 * @param[in] pelAdded - true if the PEL was added, false if removed 522 */ 523 void updateRepoStats(const PELAttributes& pel, bool pelAdded); 524 525 enum class SortOrder 526 { 527 ascending, 528 descending 529 }; 530 531 /** 532 * @brief Returns a vector of all the _pelAttributes entries sorted 533 * as specified 534 * 535 * @param[in] order - If the PELs should be returned in ascending 536 * (oldest first) or descending order. 537 * 538 * @return std::vector<AttributesReference> - The sorted vector of 539 * references to the pair<LogID, PELAttributes> entries of 540 * _pelAttributes. 541 */ 542 std::vector<AttributesReference> getAllPELAttributes(SortOrder order) const; 543 544 using IsOverLimitFunc = std::function<bool()>; 545 using IsPELTypeFunc = std::function<bool(const PELAttributes&)>; 546 547 /** 548 * @brief Makes 4 passes on the PELs that meet the IsPELTypeFunc 549 * criteria removing PELs until IsOverLimitFunc returns false. 550 * 551 * Pass 1: only delete HMC acked PELs 552 * Pass 2: only delete Os acked PELs 553 * Pass 3: only delete PHYP sent PELs 554 * Pass 4: delete all PELs 555 * 556 * @param[in] isOverLimit - The bool(void) function that should 557 * return true if PELs still need to be 558 * removed. 559 * @param[in] isPELType - The bool(const PELAttributes&) function 560 * used to select the PELs to operate on. 561 * 562 * @param[out] removedBMCLogIDs - The OpenBMC event log IDs of the 563 * removed PELs. 564 */ 565 void removePELs(IsOverLimitFunc& isOverLimit, IsPELTypeFunc& isPELType, 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