summaryrefslogtreecommitdiffstats
path: root/extensions/openpower-pels/host_notifier.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/openpower-pels/host_notifier.hpp')
-rw-r--r--extensions/openpower-pels/host_notifier.hpp319
1 files changed, 319 insertions, 0 deletions
diff --git a/extensions/openpower-pels/host_notifier.hpp b/extensions/openpower-pels/host_notifier.hpp
new file mode 100644
index 0000000..cad2b36
--- /dev/null
+++ b/extensions/openpower-pels/host_notifier.hpp
@@ -0,0 +1,319 @@
+#pragma once
+
+#include "host_interface.hpp"
+#include "pel.hpp"
+#include "repository.hpp"
+
+#include <deque>
+#include <sdeventplus/clock.hpp>
+#include <sdeventplus/source/event.hpp>
+#include <sdeventplus/utility/timer.hpp>
+
+namespace openpower::pels
+{
+
+/**
+ * @class HostNotifier
+ *
+ * This class handles notifying the host firmware of new PELs.
+ *
+ * It uses the Repository class's subscription feature to be
+ * notified about new PELs.
+ *
+ * Some PELs do not need to be sent - see enqueueRequired() and
+ * notifyRequired().
+ *
+ * The high level good path flow for sending a single PEL is:
+ *
+ * 1) Send the ID and size of the new PEL to the host.
+ * - The command response is asynchronous.
+ *
+ * 2) The host reads the raw PEL data (outside of this class).
+ *
+ * 3) The host sends the PEL to the OS, and then sends an AckPEL
+ * PLDM command to the PLDM daemon, who makes a D-Bus method
+ * call to this daemon, which calls HostNotifer::ackPEL().
+ *
+ * After this, a PEL never needs to be sent again, but if the
+ * host is rebooted before the ack comes it will.
+ *
+ * The host firmware has a finite amount of space to store PELs before
+ * sending to the OS, and it's possible it will fill up. In this case,
+ * the AckPEL command will have a special response that will tell the
+ * PLDM daemon to call HostReject D-Bus method on this daemon instead
+ * which will invoke HostNotifier::setHostFull(). This will stop new
+ * PELs from being sent, and the first PEL that hits this will have
+ * a timer set to retry again later.
+ */
+class HostNotifier
+{
+ public:
+ HostNotifier() = delete;
+ HostNotifier(const HostNotifier&) = delete;
+ HostNotifier& operator=(const HostNotifier&) = delete;
+ HostNotifier(HostNotifier&&) = delete;
+ HostNotifier& operator=(HostNotifier&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] repo - The PEL repository object
+ * @param[in] dataIface - The data interface object
+ * @param[in] hostIface - The host interface object
+ */
+ HostNotifier(Repository& repo, DataInterfaceBase& dataIface,
+ std::unique_ptr<HostInterface> hostIface);
+
+ /**
+ * @brief Destructor
+ */
+ ~HostNotifier();
+
+ /**
+ * @brief Returns the PEL queue size.
+ *
+ * For testing.
+ *
+ * @return size_t - The queue size
+ */
+ size_t queueSize() const
+ {
+ return _pelQueue.size();
+ }
+
+ /**
+ * @brief Specifies if the PEL needs to go onto the queue to be
+ * set to the host.
+ *
+ * Only returns false if:
+ * - Already acked by the host (or they didn't like it)
+ * - Hidden and the HMC already got it
+ * - The 'do not report to host' bit is set
+ *
+ * @param[in] id - The PEL ID
+ *
+ * @return bool - If enqueue is required
+ */
+ bool enqueueRequired(uint32_t id) const;
+
+ /**
+ * @brief If the host still needs to be notified of the PEL
+ * at the time of the notification.
+ *
+ * Only returns false if:
+ * - Already acked by the host
+ * - It's hidden, and the HMC already got or will get it.
+ *
+ * @param[in] id - The PEL ID
+ *
+ * @return bool - If the notify is required
+ */
+ bool notifyRequired(uint32_t id) const;
+
+ /**
+ * @brief Called when the host sends the 'ack' PLDM command.
+ *
+ * This means the PEL never needs to be sent up again.
+ *
+ * If the host was previously full, it is also an indication
+ * it no longer is.
+ *
+ * @param[in] id - The PEL ID
+ */
+ void ackPEL(uint32_t id);
+
+ /**
+ * @brief Called when the host does not have room for more
+ * PELs at this time.
+ *
+ * This can happen when an OS isn't running yet, and the
+ * staging area to hold the PELs before sending them up
+ * to the OS is full. This will stop future PEls from being
+ * sent up, as explained below.
+ *
+ * The PEL with this ID will need to be sent again, so its
+ * state is set back to 'new', and it is removed from the list
+ * of already sent PELs.
+ *
+ * A timer will be started, if it isn't already running, to
+ * issue another send in the hopes that space has been freed
+ * up by then (Receiving an ackPEL response is also an
+ * indication of this if there happened to have been other
+ * PELs in flight).
+ *
+ * @param[in] id - The PEL ID
+ */
+ void setHostFull(uint32_t id);
+
+ /**
+ * @brief Called when the host receives a malformed PEL.
+ *
+ * Ideally this will never happen, as the Repository
+ * class already purges malformed PELs.
+ *
+ * The PEL should never be sent up again.
+ *
+ * @param[in] id - The PEL ID
+ */
+ void setBadPEL(uint32_t id);
+
+ private:
+ /**
+ * @brief This function gets called by the Repository class
+ * when a new PEL is added to it.
+ *
+ * This function puts the PEL on the queue to be sent up if it
+ * needs it, and possibly dispatch the send if the conditions call
+ * for it.
+ *
+ * @param[in] pel - The new PEL
+ */
+ void newLogCallback(const PEL& pel);
+
+ /**
+ * @brief This function runs on every existing PEL at startup
+ * and puts the PEL on the queue to send if necessary.
+ *
+ * @param[in] pel - The PEL
+ *
+ * @return bool - This is an indicator to the Repository::for_each
+ * function to traverse every PEL. Always false.
+ */
+ bool addPELToQueue(const PEL& pel);
+
+ /**
+ * @brief Takes the first PEL from the queue that needs to be
+ * sent, and issues the send if conditions are right.
+ */
+ void doNewLogNotify();
+
+ /**
+ * @brief Creates the event object to handle sending the PLDM
+ * command from the event loop.
+ */
+ void scheduleDispatch();
+
+ /**
+ * @brief Kicks off the PLDM send, but called from the event
+ * loop.
+ *
+ * @param[in] source - The event source object
+ */
+ void dispatch(sdeventplus::source::EventBase& source);
+
+ /**
+ * @brief Called when the host changes state.
+ *
+ * If the new state is host up and there are PELs to send, it
+ * will trigger the first command. If the new state is off, then
+ * it will transfer any PELs that were sent but not acked yet back
+ * to the queue to be sent again.
+ *
+ * @param[in] hostUp - The new host state
+ */
+ void hostStateChange(bool hostUp);
+
+ /**
+ * @brief The callback function invoked after the asynchronous
+ * PLDM receive function is complete.
+ *
+ * If the command was successful, the state of that PEL will
+ * be set to 'sent', and the next send will be triggered.
+ *
+ * If the command failed, a retry timer will be started so it
+ * can be sent again.
+ *
+ * @param[in] status - The response status
+ */
+ void commandResponse(ResponseStatus status);
+
+ /**
+ * @brief The function called when the command failure retry
+ * time is up.
+ *
+ * It will issue a send of the previous PEL and increment the
+ * retry count.
+ */
+ void retryTimerExpired();
+
+ /**
+ * @brief The function called when the 'host full' retry timer
+ * expires.
+ *
+ * This will re-issue a command to try again with the PEL at
+ * the front of the queue.
+ */
+ void hostFullTimerExpired();
+
+ /**
+ * @brief Stops an in progress command
+ *
+ * In progress meaning after the send but before the response.
+ */
+ void stopCommand();
+
+ /**
+ * @brief The PEL repository object
+ */
+ Repository& _repo;
+
+ /**
+ * @brief The data interface object
+ */
+ DataInterfaceBase& _dataIface;
+
+ /**
+ * @brief Base class pointer for the host command interface
+ */
+ std::unique_ptr<HostInterface> _hostIface;
+
+ /**
+ * @brief The list of PEL IDs that need to be sent.
+ */
+ std::deque<uint32_t> _pelQueue;
+
+ /**
+ * @brief The list of IDs that were sent, but not acked yet.
+ *
+ * These move back to _pelQueue on a power off.
+ */
+ std::vector<uint32_t> _sentPELs;
+
+ /**
+ * @brief The ID the PEL where the notification has
+ * been kicked off but the asynchronous response
+ * hasn't been received yet.
+ */
+ uint32_t _inProgressPEL = 0;
+
+ /**
+ * @brief The command retry count
+ */
+ size_t _retryCount = 0;
+
+ /**
+ * @brief Indicates if the host has said it is full and does not
+ * currently have the space for more PELs.
+ */
+ bool _hostFull = false;
+
+ /**
+ * @brief The command retry timer.
+ */
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
+
+ /**
+ * @brief The host full timer, used to retry sending a PEL if the host
+ * said it is full.
+ */
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer;
+
+ /**
+ * @brief The object used to dispatch a new PEL send from the
+ * event loop, so the calling function can be returned from
+ * first.
+ */
+ std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
+};
+
+} // namespace openpower::pels
OpenPOWER on IntegriCloud