1f60ac27eSMatt Spinler #pragma once
2f60ac27eSMatt Spinler 
3f60ac27eSMatt Spinler #include "host_interface.hpp"
4f60ac27eSMatt Spinler #include "pel.hpp"
5f60ac27eSMatt Spinler #include "repository.hpp"
6f60ac27eSMatt Spinler 
7f869fcf8SMatt Spinler #include <sdeventplus/clock.hpp>
87d800a4eSMatt Spinler #include <sdeventplus/source/event.hpp>
9f869fcf8SMatt Spinler #include <sdeventplus/utility/timer.hpp>
10f60ac27eSMatt Spinler 
11*2544b419SPatrick Williams #include <deque>
12*2544b419SPatrick Williams 
13f60ac27eSMatt Spinler namespace openpower::pels
14f60ac27eSMatt Spinler {
15f60ac27eSMatt Spinler 
16f60ac27eSMatt Spinler /**
17f60ac27eSMatt Spinler  * @class HostNotifier
18f60ac27eSMatt Spinler  *
19f60ac27eSMatt Spinler  * This class handles notifying the host firmware of new PELs.
200e1593ecSMatt Spinler  *
210e1593ecSMatt Spinler  * It uses the Repository class's subscription feature to be
220e1593ecSMatt Spinler  * notified about new PELs.
230e1593ecSMatt Spinler  *
240e1593ecSMatt Spinler  * Some PELs do not need to be sent - see enqueueRequired() and
250e1593ecSMatt Spinler  * notifyRequired().
260e1593ecSMatt Spinler  *
270e1593ecSMatt Spinler  * The high level good path flow for sending a single PEL is:
280e1593ecSMatt Spinler  *
290e1593ecSMatt Spinler  * 1) Send the ID and size of the new PEL to the host.
300e1593ecSMatt Spinler  *   - The command response is asynchronous.
310e1593ecSMatt Spinler  *
320e1593ecSMatt Spinler  * 2) The host reads the raw PEL data (outside of this class).
330e1593ecSMatt Spinler  *
340e1593ecSMatt Spinler  * 3) The host sends the PEL to the OS, and then sends an AckPEL
350e1593ecSMatt Spinler  *    PLDM command to the PLDM daemon, who makes a D-Bus method
360e1593ecSMatt Spinler  *    call to this daemon, which calls HostNotifer::ackPEL().
370e1593ecSMatt Spinler  *
380e1593ecSMatt Spinler  *    After this, a PEL never needs to be sent again, but if the
390e1593ecSMatt Spinler  *    host is rebooted before the ack comes it will.
400e1593ecSMatt Spinler  *
410e1593ecSMatt Spinler  * The host firmware has a finite amount of space to store PELs before
420e1593ecSMatt Spinler  * sending to the OS, and it's possible it will fill up.  In this case,
430e1593ecSMatt Spinler  * the AckPEL command will have a special response that will tell the
440e1593ecSMatt Spinler  * PLDM daemon to call  HostReject D-Bus method on this daemon instead
450e1593ecSMatt Spinler  * which will invoke HostNotifier::setHostFull(). This will stop new
460e1593ecSMatt Spinler  * PELs from being sent, and the first PEL that hits this will have
470e1593ecSMatt Spinler  * a timer set to retry again later.
48f60ac27eSMatt Spinler  */
49f60ac27eSMatt Spinler class HostNotifier
50f60ac27eSMatt Spinler {
51f60ac27eSMatt Spinler   public:
52f60ac27eSMatt Spinler     HostNotifier() = delete;
53f60ac27eSMatt Spinler     HostNotifier(const HostNotifier&) = delete;
54f60ac27eSMatt Spinler     HostNotifier& operator=(const HostNotifier&) = delete;
55f60ac27eSMatt Spinler     HostNotifier(HostNotifier&&) = delete;
56f60ac27eSMatt Spinler     HostNotifier& operator=(HostNotifier&&) = delete;
57f60ac27eSMatt Spinler 
58f60ac27eSMatt Spinler     /**
59f60ac27eSMatt Spinler      * @brief Constructor
60f60ac27eSMatt Spinler      *
61f60ac27eSMatt Spinler      * @param[in] repo - The PEL repository object
62f60ac27eSMatt Spinler      * @param[in] dataIface - The data interface object
63f60ac27eSMatt Spinler      * @param[in] hostIface - The host interface object
64f60ac27eSMatt Spinler      */
65f60ac27eSMatt Spinler     HostNotifier(Repository& repo, DataInterfaceBase& dataIface,
66f60ac27eSMatt Spinler                  std::unique_ptr<HostInterface> hostIface);
67f60ac27eSMatt Spinler 
68f60ac27eSMatt Spinler     /**
69f60ac27eSMatt Spinler      * @brief Destructor
70f60ac27eSMatt Spinler      */
71f60ac27eSMatt Spinler     ~HostNotifier();
72f60ac27eSMatt Spinler 
73f60ac27eSMatt Spinler     /**
74f60ac27eSMatt Spinler      * @brief Returns the PEL queue size.
75f60ac27eSMatt Spinler      *
76f60ac27eSMatt Spinler      * For testing.
77f60ac27eSMatt Spinler      *
78f60ac27eSMatt Spinler      * @return size_t - The queue size
79f60ac27eSMatt Spinler      */
queueSize() const80f60ac27eSMatt Spinler     size_t queueSize() const
81f60ac27eSMatt Spinler     {
82f60ac27eSMatt Spinler         return _pelQueue.size();
83f60ac27eSMatt Spinler     }
84f60ac27eSMatt Spinler 
85f60ac27eSMatt Spinler     /**
86f60ac27eSMatt Spinler      * @brief Specifies if the PEL needs to go onto the queue to be
87f60ac27eSMatt Spinler      *        set to the host.
88f60ac27eSMatt Spinler      *
89a943b15bSMatt Spinler      * Only returns false if:
90a943b15bSMatt Spinler      *  - Already acked by the host (or they didn't like it)
91a943b15bSMatt Spinler      *  - Hidden and the HMC already got it
92a943b15bSMatt Spinler      *  - The 'do not report to host' bit is set
93a943b15bSMatt Spinler      *
94f60ac27eSMatt Spinler      * @param[in] id - The PEL ID
95f60ac27eSMatt Spinler      *
96f60ac27eSMatt Spinler      * @return bool - If enqueue is required
97f60ac27eSMatt Spinler      */
98f60ac27eSMatt Spinler     bool enqueueRequired(uint32_t id) const;
99f60ac27eSMatt Spinler 
100f77debb9SMatt Spinler     /**
101f77debb9SMatt Spinler      * @brief If the host still needs to be notified of the PEL
102f77debb9SMatt Spinler      *        at the time of the notification.
103f77debb9SMatt Spinler      *
104f77debb9SMatt Spinler      * Only returns false if:
105f77debb9SMatt Spinler      *  - Already acked by the host
106f77debb9SMatt Spinler      *  - It's hidden, and the HMC already got or will get it.
107f77debb9SMatt Spinler      *
108f77debb9SMatt Spinler      * @param[in] id - The PEL ID
109f77debb9SMatt Spinler      *
110f77debb9SMatt Spinler      * @return bool - If the notify is required
111f77debb9SMatt Spinler      */
112f77debb9SMatt Spinler     bool notifyRequired(uint32_t id) const;
113f77debb9SMatt Spinler 
114cc3b64aeSMatt Spinler     /**
115cc3b64aeSMatt Spinler      * @brief Called when the host sends the 'ack' PLDM command.
116cc3b64aeSMatt Spinler      *
117cc3b64aeSMatt Spinler      * This means the PEL never needs to be sent up again.
118cc3b64aeSMatt Spinler      *
11941293cb8SMatt Spinler      * If the host was previously full, it is also an indication
12041293cb8SMatt Spinler      * it no longer is.
12141293cb8SMatt Spinler      *
122cc3b64aeSMatt Spinler      * @param[in] id - The PEL ID
123cc3b64aeSMatt Spinler      */
124cc3b64aeSMatt Spinler     void ackPEL(uint32_t id);
125cc3b64aeSMatt Spinler 
12641293cb8SMatt Spinler     /**
12741293cb8SMatt Spinler      * @brief Called when the host does not have room for more
12841293cb8SMatt Spinler      *        PELs at this time.
12941293cb8SMatt Spinler      *
13041293cb8SMatt Spinler      * This can happen when an OS isn't running yet, and the
13141293cb8SMatt Spinler      * staging area to hold the PELs before sending them up
13241293cb8SMatt Spinler      * to the OS is full.  This will stop future PEls from being
13341293cb8SMatt Spinler      * sent up, as explained below.
13441293cb8SMatt Spinler      *
13541293cb8SMatt Spinler      * The PEL with this ID will need to be sent again, so its
13641293cb8SMatt Spinler      * state is set back to 'new', and it is removed from the list
13741293cb8SMatt Spinler      * of already sent PELs.
13841293cb8SMatt Spinler      *
13941293cb8SMatt Spinler      * A timer will be started, if it isn't already running, to
14041293cb8SMatt Spinler      * issue another send in the hopes that space has been freed
14141293cb8SMatt Spinler      * up by then (Receiving an ackPEL response is also an
14241293cb8SMatt Spinler      * indication of this if there happened to have been other
14341293cb8SMatt Spinler      * PELs in flight).
14441293cb8SMatt Spinler      *
14541293cb8SMatt Spinler      * @param[in] id - The PEL ID
14641293cb8SMatt Spinler      */
14741293cb8SMatt Spinler     void setHostFull(uint32_t id);
14841293cb8SMatt Spinler 
149a19b6234SMatt Spinler     /**
150a19b6234SMatt Spinler      * @brief Called when the host receives a malformed PEL.
151a19b6234SMatt Spinler      *
152a19b6234SMatt Spinler      * Ideally this will never happen, as the Repository
153a19b6234SMatt Spinler      * class already purges malformed PELs.
154a19b6234SMatt Spinler      *
155a19b6234SMatt Spinler      * The PEL should never be sent up again.
156a19b6234SMatt Spinler      *
157a19b6234SMatt Spinler      * @param[in] id - The PEL ID
158a19b6234SMatt Spinler      */
159a19b6234SMatt Spinler     void setBadPEL(uint32_t id);
160a19b6234SMatt Spinler 
161f60ac27eSMatt Spinler   private:
162f60ac27eSMatt Spinler     /**
163f60ac27eSMatt Spinler      * @brief This function gets called by the Repository class
164f60ac27eSMatt Spinler      *        when a new PEL is added to it.
165f60ac27eSMatt Spinler      *
1667d800a4eSMatt Spinler      * This function puts the PEL on the queue to be sent up if it
1677d800a4eSMatt Spinler      * needs it, and possibly dispatch the send if the conditions call
1687d800a4eSMatt Spinler      * for it.
1697d800a4eSMatt Spinler      *
170f60ac27eSMatt Spinler      * @param[in] pel - The new PEL
171f60ac27eSMatt Spinler      */
172f60ac27eSMatt Spinler     void newLogCallback(const PEL& pel);
173f60ac27eSMatt Spinler 
174f60ac27eSMatt Spinler     /**
1757cb985ffSMatt Spinler      * @brief This function gets called by the Repository class
1767cb985ffSMatt Spinler      *        when a PEL is deleted.
1777cb985ffSMatt Spinler      *
1787cb985ffSMatt Spinler      * The deleted ID will be removed from the PEL queue and the
1797cb985ffSMatt Spinler      * sent list.
1807cb985ffSMatt Spinler      *
1817cb985ffSMatt Spinler      * @param[in] id - The deleted PEL ID
1827cb985ffSMatt Spinler      */
1837cb985ffSMatt Spinler     void deleteLogCallback(uint32_t id);
1847cb985ffSMatt Spinler 
1857cb985ffSMatt Spinler     /**
186f60ac27eSMatt Spinler      * @brief This function runs on every existing PEL at startup
187f60ac27eSMatt Spinler      *        and puts the PEL on the queue to send if necessary.
188f60ac27eSMatt Spinler      *
189f60ac27eSMatt Spinler      * @param[in] pel - The PEL
190f60ac27eSMatt Spinler      *
191f60ac27eSMatt Spinler      * @return bool - This is an indicator to the Repository::for_each
192f60ac27eSMatt Spinler      *                function to traverse every PEL.  Always false.
193f60ac27eSMatt Spinler      */
194f60ac27eSMatt Spinler     bool addPELToQueue(const PEL& pel);
195f60ac27eSMatt Spinler 
196f60ac27eSMatt Spinler     /**
197f77debb9SMatt Spinler      * @brief Takes the first PEL from the queue that needs to be
198f77debb9SMatt Spinler      *        sent, and issues the send if conditions are right.
199f60ac27eSMatt Spinler      */
200f60ac27eSMatt Spinler     void doNewLogNotify();
201f60ac27eSMatt Spinler 
202f60ac27eSMatt Spinler     /**
2037d800a4eSMatt Spinler      * @brief Creates the event object to handle sending the PLDM
2047d800a4eSMatt Spinler      *        command from the event loop.
2057d800a4eSMatt Spinler      */
2067d800a4eSMatt Spinler     void scheduleDispatch();
2077d800a4eSMatt Spinler 
2087d800a4eSMatt Spinler     /**
2097d800a4eSMatt Spinler      * @brief Kicks off the PLDM send, but called from the event
2107d800a4eSMatt Spinler      *        loop.
2117d800a4eSMatt Spinler      *
2127d800a4eSMatt Spinler      * @param[in] source - The event source object
2137d800a4eSMatt Spinler      */
2147d800a4eSMatt Spinler     void dispatch(sdeventplus::source::EventBase& source);
2157d800a4eSMatt Spinler 
2167d800a4eSMatt Spinler     /**
217f60ac27eSMatt Spinler      * @brief Called when the host changes state.
218f60ac27eSMatt Spinler      *
2193019c6fbSMatt Spinler      * If the new state is host up and there are PELs to send, it
2203019c6fbSMatt Spinler      * will trigger the first command.  If the new state is off, then
2213019c6fbSMatt Spinler      * it will transfer any PELs that were sent but not acked yet back
2223019c6fbSMatt Spinler      * to the queue to be sent again.
2233019c6fbSMatt Spinler      *
224f60ac27eSMatt Spinler      * @param[in] hostUp - The new host state
225f60ac27eSMatt Spinler      */
226f60ac27eSMatt Spinler     void hostStateChange(bool hostUp);
227f60ac27eSMatt Spinler 
228f60ac27eSMatt Spinler     /**
229f60ac27eSMatt Spinler      * @brief The callback function invoked after the asynchronous
230f60ac27eSMatt Spinler      *        PLDM receive function is complete.
231f60ac27eSMatt Spinler      *
232f869fcf8SMatt Spinler      * If the command was successful, the state of that PEL will
233f869fcf8SMatt Spinler      * be set to 'sent', and the next send will be triggered.
234f869fcf8SMatt Spinler      *
235f869fcf8SMatt Spinler      * If the command failed, a retry timer will be started so it
236f869fcf8SMatt Spinler      * can be sent again.
237f869fcf8SMatt Spinler      *
238f60ac27eSMatt Spinler      * @param[in] status - The response status
239f60ac27eSMatt Spinler      */
240f60ac27eSMatt Spinler     void commandResponse(ResponseStatus status);
241f60ac27eSMatt Spinler 
242f60ac27eSMatt Spinler     /**
243f869fcf8SMatt Spinler      * @brief The function called when the command failure retry
244f869fcf8SMatt Spinler      *        time is up.
245f869fcf8SMatt Spinler      *
246f869fcf8SMatt Spinler      * It will issue a send of the previous PEL and increment the
247f869fcf8SMatt Spinler      * retry count.
248f869fcf8SMatt Spinler      */
249f869fcf8SMatt Spinler     void retryTimerExpired();
250f869fcf8SMatt Spinler 
251f869fcf8SMatt Spinler     /**
25241293cb8SMatt Spinler      * @brief The function called when the 'host full' retry timer
25341293cb8SMatt Spinler      *        expires.
25441293cb8SMatt Spinler      *
25541293cb8SMatt Spinler      * This will re-issue a command to try again with the PEL at
25641293cb8SMatt Spinler      * the front of the queue.
25741293cb8SMatt Spinler      */
25841293cb8SMatt Spinler     void hostFullTimerExpired();
25941293cb8SMatt Spinler 
26041293cb8SMatt Spinler     /**
261e5f7508bSMatt Spinler      * @brief The function called when the host up retry timer
262e5f7508bSMatt Spinler      *        expires.
263e5f7508bSMatt Spinler      *
264e5f7508bSMatt Spinler      * It kicks off sending the new log commands.
265e5f7508bSMatt Spinler      */
266e5f7508bSMatt Spinler     void hostUpTimerExpired();
267e5f7508bSMatt Spinler 
268e5f7508bSMatt Spinler     /**
2693019c6fbSMatt Spinler      * @brief Stops an in progress command
2703019c6fbSMatt Spinler      *
2713019c6fbSMatt Spinler      * In progress meaning after the send but before the response.
2723019c6fbSMatt Spinler      */
2733019c6fbSMatt Spinler     void stopCommand();
2743019c6fbSMatt Spinler 
2753019c6fbSMatt Spinler     /**
276f60ac27eSMatt Spinler      * @brief The PEL repository object
277f60ac27eSMatt Spinler      */
278f60ac27eSMatt Spinler     Repository& _repo;
279f60ac27eSMatt Spinler 
280f60ac27eSMatt Spinler     /**
281f60ac27eSMatt Spinler      * @brief The data interface object
282f60ac27eSMatt Spinler      */
283f60ac27eSMatt Spinler     DataInterfaceBase& _dataIface;
284f60ac27eSMatt Spinler 
285f60ac27eSMatt Spinler     /**
286f60ac27eSMatt Spinler      * @brief Base class pointer for the host command interface
287f60ac27eSMatt Spinler      */
288f60ac27eSMatt Spinler     std::unique_ptr<HostInterface> _hostIface;
289f60ac27eSMatt Spinler 
290f60ac27eSMatt Spinler     /**
291f60ac27eSMatt Spinler      * @brief The list of PEL IDs that need to be sent.
292f60ac27eSMatt Spinler      */
293f60ac27eSMatt Spinler     std::deque<uint32_t> _pelQueue;
294f869fcf8SMatt Spinler 
295f869fcf8SMatt Spinler     /**
296f869fcf8SMatt Spinler      * @brief The list of IDs that were sent, but not acked yet.
297f869fcf8SMatt Spinler      *
298f869fcf8SMatt Spinler      * These move back to _pelQueue on a power off.
299f869fcf8SMatt Spinler      */
300f869fcf8SMatt Spinler     std::vector<uint32_t> _sentPELs;
301f869fcf8SMatt Spinler 
302f869fcf8SMatt Spinler     /**
303f869fcf8SMatt Spinler      * @brief The ID the PEL where the notification has
304f869fcf8SMatt Spinler      *        been kicked off but the asynchronous response
305f869fcf8SMatt Spinler      *        hasn't been received yet.
306f869fcf8SMatt Spinler      */
307f869fcf8SMatt Spinler     uint32_t _inProgressPEL = 0;
308f869fcf8SMatt Spinler 
309f869fcf8SMatt Spinler     /**
310f869fcf8SMatt Spinler      * @brief The command retry count
311f869fcf8SMatt Spinler      */
312f869fcf8SMatt Spinler     size_t _retryCount = 0;
313f869fcf8SMatt Spinler 
314f869fcf8SMatt Spinler     /**
31541293cb8SMatt Spinler      * @brief Indicates if the host has said it is full and does not
31641293cb8SMatt Spinler      *        currently have the space for more PELs.
31741293cb8SMatt Spinler      */
31841293cb8SMatt Spinler     bool _hostFull = false;
31941293cb8SMatt Spinler 
32041293cb8SMatt Spinler     /**
321f869fcf8SMatt Spinler      * @brief The command retry timer.
322f869fcf8SMatt Spinler      */
323f869fcf8SMatt Spinler     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
3247d800a4eSMatt Spinler 
3257d800a4eSMatt Spinler     /**
32641293cb8SMatt Spinler      * @brief The host full timer, used to retry sending a PEL if the host
32741293cb8SMatt Spinler      *        said it is full.
32841293cb8SMatt Spinler      */
32941293cb8SMatt Spinler     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer;
33041293cb8SMatt Spinler 
33141293cb8SMatt Spinler     /**
332e5f7508bSMatt Spinler      * @brief The host up timer, used to kick off sending commands to the
333e5f7508bSMatt Spinler      *        host after a delay after the host is up.
334e5f7508bSMatt Spinler      */
335e5f7508bSMatt Spinler     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostUpTimer;
336e5f7508bSMatt Spinler 
337e5f7508bSMatt Spinler     /**
3387d800a4eSMatt Spinler      * @brief The object used to dispatch a new PEL send from the
3397d800a4eSMatt Spinler      *        event loop, so the calling function can be returned from
3407d800a4eSMatt Spinler      *        first.
3417d800a4eSMatt Spinler      */
3427d800a4eSMatt Spinler     std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
343f60ac27eSMatt Spinler };
344f60ac27eSMatt Spinler 
345f60ac27eSMatt Spinler } // namespace openpower::pels
346