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