blob: c7cb39a82dc770681242c60bcc0479dce615339e [file] [log] [blame]
Matt Spinler89fa0822019-07-17 13:54:30 -05001#pragma once
2#include "bcd_time.hpp"
Matt Spinler8d5f3a22020-07-07 10:30:33 -05003#include "paths.hpp"
Matt Spinler89fa0822019-07-17 13:54:30 -05004#include "pel.hpp"
5
6#include <algorithm>
Matt Spinler0ff00482019-11-06 16:19:46 -06007#include <bitset>
Matt Spinler89fa0822019-07-17 13:54:30 -05008#include <filesystem>
Matt Spinler475e5742019-07-18 16:09:49 -05009#include <map>
Matt Spinler89fa0822019-07-17 13:54:30 -050010
11namespace openpower
12{
13namespace pels
14{
15
16/**
17 * @class Repository
18 *
19 * The class handles saving and retrieving PELs on the BMC.
20 */
21class Repository
22{
23 public:
Matt Spinler475e5742019-07-18 16:09:49 -050024 /**
Matt Spinler0ff00482019-11-06 16:19:46 -060025 * @brief Structure of commonly used PEL attributes.
26 */
27 struct PELAttributes
28 {
29 std::filesystem::path path;
Matt Spinlerdd325c32020-07-07 11:01:54 -050030 size_t sizeOnDisk;
31 uint8_t creator;
32 uint8_t severity;
Matt Spinler0ff00482019-11-06 16:19:46 -060033 std::bitset<16> actionFlags;
Matt Spinler346f99a2019-11-21 13:06:35 -060034 TransmissionState hostState;
35 TransmissionState hmcState;
Matt Spinler0ff00482019-11-06 16:19:46 -060036
37 PELAttributes() = delete;
38
Matt Spinlerdd325c32020-07-07 11:01:54 -050039 PELAttributes(const std::filesystem::path& p, size_t size,
40 uint8_t creator, uint8_t sev, uint16_t flags,
Matt Spinler346f99a2019-11-21 13:06:35 -060041 TransmissionState hostState, TransmissionState hmcState) :
42 path(p),
Matt Spinlerdd325c32020-07-07 11:01:54 -050043 sizeOnDisk(size), creator(creator), severity(sev),
Matt Spinler346f99a2019-11-21 13:06:35 -060044 actionFlags(flags), hostState(hostState), hmcState(hmcState)
Matt Spinler0ff00482019-11-06 16:19:46 -060045 {
46 }
47 };
48
49 /**
Matt Spinler475e5742019-07-18 16:09:49 -050050 * @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
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500115 using AttributesReference =
116 std::reference_wrapper<const std::pair<const LogID, PELAttributes>>;
117
Matt Spinlerb188f782020-07-07 11:18:12 -0500118 /**
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
Matt Spinler89fa0822019-07-17 13:54:30 -0500139 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 */
Matt Spinler8d5f3a22020-07-07 10:30:33 -0500151 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);
Matt Spinler89fa0822019-07-17 13:54:30 -0500166
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 /**
Matt Spinler475e5742019-07-18 16:09:49 -0500177 * @brief Removes a PEL from the repository
178 *
179 * @param[in] id - the ID (either the pel ID, OBMC ID, or both) to remove
180 */
181 void remove(const LogID& id);
182
183 /**
Matt Spinler89fa0822019-07-17 13:54:30 -0500184 * @brief Generates the filename to use for the PEL ID and BCDTime.
185 *
186 * @param[in] pelID - the PEL ID
187 * @param[in] time - the BCD time
188 *
189 * @return string - A filename string of <BCD_time>_<pelID>
190 */
191 static std::string getPELFilename(uint32_t pelID, const BCDTime& time);
192
Matt Spinler475e5742019-07-18 16:09:49 -0500193 /**
194 * @brief Returns true if the PEL with the specified ID is in the repo.
195 *
196 * @param[in] id - the ID (either the pel ID, OBMC ID, or both)
197 * @return bool - true if that PEL is present
198 */
199 inline bool hasPEL(const LogID& id)
200 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600201 return findPEL(id) != _pelAttributes.end();
Matt Spinler475e5742019-07-18 16:09:49 -0500202 }
203
Matt Spinler2813f362019-07-19 12:45:28 -0500204 /**
205 * @brief Returns the PEL data based on its ID.
206 *
207 * If the data can't be found for that ID, then the optional object
208 * will be empty.
209 *
210 * @param[in] id - the LogID to get the PEL for, which can be either a
211 * PEL ID or OpenBMC log ID.
212 * @return std::optional<std::vector<uint8_t>> - the PEL data
213 */
214 std::optional<std::vector<uint8_t>> getPELData(const LogID& id);
215
Matt Spinler6d512242019-12-09 13:44:17 -0600216 /**
217 * @brief Get a file descriptor to the PEL data
218 *
219 * @param[in] id - The ID to get the FD for
220 *
221 * @return std::optional<sdbusplus::message::unix_fd> -
222 * The FD, or an empty optional object.
223 */
224 std::optional<sdbusplus::message::unix_fd> getPELFD(const LogID& id);
225
Matt Spinler1ea78802019-11-01 13:04:59 -0500226 using ForEachFunc = std::function<bool(const PEL&)>;
227
228 /**
229 * @brief Run a user defined function on every PEL in the repository.
230 *
231 * ForEachFunc takes a const PEL reference, and should return
232 * true to stop iterating and return out of for_each.
233 *
234 * For example, to save up to 100 IDs in the repo into a vector:
235 *
236 * std::vector<uint32_t> ids;
237 * ForEachFunc f = [&ids](const PEL& pel) {
238 * ids.push_back(pel.id());
239 * return ids.size() == 100 ? true : false;
240 * };
241 *
242 * @param[in] func - The function to run.
243 */
244 void for_each(ForEachFunc func) const;
245
Matt Spinler421f6532019-11-06 15:40:45 -0600246 using AddCallback = std::function<void(const PEL&)>;
247
248 /**
249 * @brief Subscribe to PELs being added to the repository.
250 *
251 * Every time a PEL is added to the repository, the provided
252 * function will be called with the new PEL as the argument.
253 *
254 * The function must be of type void(const PEL&).
255 *
256 * @param[in] name - The subscription name
257 * @param[in] func - The callback function
258 */
259 void subscribeToAdds(const std::string& name, AddCallback func)
260 {
261 if (_addSubscriptions.find(name) == _addSubscriptions.end())
262 {
263 _addSubscriptions.emplace(name, func);
264 }
265 }
266
267 /**
268 * @brief Unsubscribe from new PELs.
269 *
270 * @param[in] name - The subscription name
271 */
272 void unsubscribeFromAdds(const std::string& name)
273 {
274 _addSubscriptions.erase(name);
275 }
276
277 using DeleteCallback = std::function<void(uint32_t)>;
278
279 /**
280 * @brief Subscribe to PELs being deleted from the repository.
281 *
282 * Every time a PEL is deleted from the repository, the provided
283 * function will be called with the PEL ID as the argument.
284 *
285 * The function must be of type void(const uint32_t).
286 *
287 * @param[in] name - The subscription name
288 * @param[in] func - The callback function
289 */
290 void subscribeToDeletes(const std::string& name, DeleteCallback func)
291 {
292 if (_deleteSubscriptions.find(name) == _deleteSubscriptions.end())
293 {
294 _deleteSubscriptions.emplace(name, func);
295 }
296 }
297
298 /**
299 * @brief Unsubscribe from deleted PELs.
300 *
301 * @param[in] name - The subscription name
302 */
303 void unsubscribeFromDeletes(const std::string& name)
304 {
305 _deleteSubscriptions.erase(name);
306 }
307
Matt Spinler0ff00482019-11-06 16:19:46 -0600308 /**
309 * @brief Get the PEL attributes for a PEL
310 *
311 * @param[in] id - The ID to find the attributes for
312 *
313 * @return The attributes or an empty optional if not found
314 */
315 std::optional<std::reference_wrapper<const PELAttributes>>
316 getPELAttributes(const LogID& id) const;
317
Matt Spinler29d18c12019-11-21 13:31:27 -0600318 /**
319 * @brief Sets the host transmission state on a PEL file
320 *
321 * Writes the host transmission state field in the User Header
322 * section in the PEL data specified by the ID.
323 *
324 * @param[in] pelID - The PEL ID
325 * @param[in] state - The state to write
326 */
327 void setPELHostTransState(uint32_t pelID, TransmissionState state);
328
329 /**
330 * @brief Sets the HMC transmission state on a PEL file
331 *
332 * Writes the HMC transmission state field in the User Header
333 * section in the PEL data specified by the ID.
334 *
335 * @param[in] pelID - The PEL ID
336 * @param[in] state - The state to write
337 */
338 void setPELHMCTransState(uint32_t pelID, TransmissionState state);
339
Matt Spinlerb188f782020-07-07 11:18:12 -0500340 /**
341 * @brief Returns the size stats structure
342 *
343 * @return const SizeStats& - The stats structure
344 */
345 const SizeStats& getSizeStats() const
346 {
347 return _sizes;
348 }
349
350 /**
351 * @brief Says if the PEL is considered serviceable (not just
352 * informational) as determined by its severity.
353 *
354 * @param[in] pel - The PELAttributes entry for the PEL
355 * @return bool - If serviceable or not
356 */
357 static bool isServiceableSev(const PELAttributes& pel);
358
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500359 /**
Matt Spinler7e727a32020-07-07 15:00:17 -0500360 * @brief Returns true if the total amount of disk space occupied
361 * by the PELs in the repo is over 95% of the maximum
362 * size, or if there are over the maximum number of
363 * PELs allowed.
364 *
365 * @return bool - true if repo is > 95% full or too many PELs
366 */
367 bool sizeWarning() const;
368
369 /**
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500370 * @brief Deletes PELs to bring the repository size down
371 * to at most 90% full by placing PELs into 4 different
372 * catogories and then removing PELs until those catogories
373 * only take up certain percentages of the allowed space.
374 *
375 * This does not delete the corresponding OpenBMC event logs, which
376 * is why those IDs are returned, so they can be deleted later.
377 *
378 * The categories and their rules are:
379 * 1) Informational BMC PELs cannot take up more than 15% of
380 * the allocated space.
381 * 2) Non-informational BMC PELs cannot take up more than 30%
382 * of the allocated space.
383 * 3) Informational non-BMC PELs cannot take up more than 15% of
384 * the allocated space.
385 * 4) Non-informational non-BMC PELs cannot take up more than 30%
386 * of the allocated space.
387 *
388 * While removing PELs in a category, 4 passes will be made, with
389 * PELs being removed oldest first during each pass.
390 *
391 * Pass 1: only delete HMC acked PELs
392 * Pass 2: only delete OS acked PELs
393 * Pass 3: only delete PHYP sent PELs
394 * Pass 4: delete all PELs
395 *
396 * @return std::vector<uint32_t> - The OpenBMC event log IDs of
397 * the PELs that were deleted.
398 */
399 std::vector<uint32_t> prune();
400
Matt Spinler89fa0822019-07-17 13:54:30 -0500401 private:
Matt Spinler29d18c12019-11-21 13:31:27 -0600402 using PELUpdateFunc = std::function<void(PEL&)>;
403
404 /**
405 * @brief Lets a function modify a PEL and saves the results
406 *
407 * Runs updateFunc (a void(PEL&) function) on the PEL data
408 * on the file specified, and writes the results back to the file.
409 *
410 * @param[in] path - The file path to use
411 * @param[in] updateFunc - The function to run to update the PEL.
412 */
413 void updatePEL(const std::filesystem::path& path, PELUpdateFunc updateFunc);
414
Matt Spinler89fa0822019-07-17 13:54:30 -0500415 /**
Matt Spinler0ff00482019-11-06 16:19:46 -0600416 * @brief Finds an entry in the _pelAttributes map.
Matt Spinler475e5742019-07-18 16:09:49 -0500417 *
418 * @param[in] id - the ID (either the pel ID, OBMC ID, or both)
419 *
420 * @return an iterator to the entry
421 */
Matt Spinler0ff00482019-11-06 16:19:46 -0600422 std::map<LogID, PELAttributes>::const_iterator
423 findPEL(const LogID& id) const
Matt Spinler475e5742019-07-18 16:09:49 -0500424 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600425 return std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
426 [&id](const auto& a) { return a.first == id; });
Matt Spinler475e5742019-07-18 16:09:49 -0500427 }
428
429 /**
Matt Spinler421f6532019-11-06 15:40:45 -0600430 * @brief Call any subscribed functions for new PELs
431 *
432 * @param[in] pel - The new PEL
433 */
434 void processAddCallbacks(const PEL& pel) const;
435
436 /**
437 * @brief Call any subscribed functions for deleted PELs
438 *
439 * @param[in] id - The ID of the deleted PEL
440 */
441 void processDeleteCallbacks(uint32_t id) const;
442
443 /**
Matt Spinler0ff00482019-11-06 16:19:46 -0600444 * @brief Restores the _pelAttributes map on startup based on the existing
Matt Spinler475e5742019-07-18 16:09:49 -0500445 * PEL data files.
446 */
447 void restore();
448
449 /**
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600450 * @brief Stores a PEL object in the filesystem.
451 *
452 * @param[in] pel - The PEL to write
453 * @param[in] path - The file to write to
454 *
455 * Throws exceptions on failures.
456 */
457 void write(const PEL& pel, const std::filesystem::path& path);
458
459 /**
Matt Spinlerb188f782020-07-07 11:18:12 -0500460 * @brief Updates the repository statistics after a PEL is
461 * added or removed.
462 *
463 * @param[in] pel - The PELAttributes entry for the PEL
464 * @param[in] pelAdded - true if the PEL was added, false if removed
465 */
466 void updateRepoStats(const PELAttributes& pel, bool pelAdded);
467
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500468 enum class SortOrder
469 {
470 ascending,
471 descending
472 };
473
474 /**
475 * @brief Returns a vector of all the _pelAttributes entries sorted
476 * as specified
477 *
478 * @param[in] order - If the PELs should be returned in ascending
479 * (oldest first) or descending order.
480 *
481 * @return std::vector<AttributesReference> - The sorted vector of
482 * references to the pair<LogID, PELAttributes> entries of
483 * _pelAttributes.
484 */
485 std::vector<AttributesReference> getAllPELAttributes(SortOrder order) const;
486
487 using IsOverLimitFunc = std::function<bool()>;
488 using IsPELTypeFunc = std::function<bool(const PELAttributes&)>;
489
490 /**
491 * @brief Makes 4 passes on the PELs that meet the IsPELTypeFunc
492 * criteria removing PELs until IsOverLimitFunc returns false.
493 *
494 * Pass 1: only delete HMC acked PELs
495 * Pass 2: only delete Os acked PELs
496 * Pass 3: only delete PHYP sent PELs
497 * Pass 4: delete all PELs
498 *
499 * @param[in] isOverLimit - The bool(void) function that should
500 * return true if PELs still need to be
501 * removed.
502 * @param[in] isPELType - The bool(const PELAttributes&) function
503 * used to select the PELs to operate on.
504 *
505 * @param[out] removedBMCLogIDs - The OpenBMC event log IDs of the
506 * removed PELs.
507 */
508 void removePELs(IsOverLimitFunc& isOverLimit, IsPELTypeFunc& isPELType,
509 std::vector<uint32_t>& removedBMCLogIDs);
Matt Spinlerb188f782020-07-07 11:18:12 -0500510 /**
Matt Spinler89fa0822019-07-17 13:54:30 -0500511 * @brief The filesystem path to the PEL logs.
512 */
513 const std::filesystem::path _logPath;
Matt Spinler475e5742019-07-18 16:09:49 -0500514
515 /**
Matt Spinler0ff00482019-11-06 16:19:46 -0600516 * @brief A map of the PEL/OBMC IDs to PEL attributes.
Matt Spinler475e5742019-07-18 16:09:49 -0500517 */
Matt Spinler0ff00482019-11-06 16:19:46 -0600518 std::map<LogID, PELAttributes> _pelAttributes;
Matt Spinler421f6532019-11-06 15:40:45 -0600519
520 /**
521 * @brief Subcriptions for new PELs.
522 */
523 std::map<std::string, AddCallback> _addSubscriptions;
524
525 /**
526 * @brief Subscriptions for deleted PELs.
527 */
528 std::map<std::string, DeleteCallback> _deleteSubscriptions;
Matt Spinler8d5f3a22020-07-07 10:30:33 -0500529
530 /**
531 * @brief The maximum amount of space that the PELs in the
532 * repository can occupy.
533 */
534 const uint64_t _maxRepoSize;
535
536 /**
537 * @brief The maximum number of PELs to allow in the repo
538 * before pruning.
539 */
540 const size_t _maxNumPELs;
Matt Spinlerb188f782020-07-07 11:18:12 -0500541
542 /**
543 * @brief Statistics on the sizes of the stored PELs.
544 */
545 SizeStats _sizes;
Matt Spinler89fa0822019-07-17 13:54:30 -0500546};
547
548} // namespace pels
549} // namespace openpower