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