blob: cef999f31b048d0a8ffe9d445ad34bdebe2b5a00 [file] [log] [blame]
Matt Spinlerf60ac272019-12-11 13:47:50 -06001#pragma once
2
3#include "host_interface.hpp"
4#include "pel.hpp"
5#include "repository.hpp"
6
Matt Spinlerf869fcf2019-12-11 15:02:20 -06007#include <sdeventplus/clock.hpp>
Matt Spinler7d800a42019-12-12 10:35:01 -06008#include <sdeventplus/source/event.hpp>
Matt Spinlerf869fcf2019-12-11 15:02:20 -06009#include <sdeventplus/utility/timer.hpp>
Matt Spinlerf60ac272019-12-11 13:47:50 -060010
Patrick Williams2544b412022-10-04 08:41:06 -050011#include <deque>
12
Matt Spinlerf60ac272019-12-11 13:47:50 -060013namespace openpower::pels
14{
15
16/**
17 * @class HostNotifier
18 *
19 * This class handles notifying the host firmware of new PELs.
Matt Spinler0e1593e2019-12-16 10:50:43 -060020 *
21 * It uses the Repository class's subscription feature to be
22 * notified about new PELs.
23 *
24 * Some PELs do not need to be sent - see enqueueRequired() and
25 * notifyRequired().
26 *
27 * The high level good path flow for sending a single PEL is:
28 *
29 * 1) Send the ID and size of the new PEL to the host.
30 * - The command response is asynchronous.
31 *
32 * 2) The host reads the raw PEL data (outside of this class).
33 *
34 * 3) The host sends the PEL to the OS, and then sends an AckPEL
35 * PLDM command to the PLDM daemon, who makes a D-Bus method
36 * call to this daemon, which calls HostNotifer::ackPEL().
37 *
38 * After this, a PEL never needs to be sent again, but if the
39 * host is rebooted before the ack comes it will.
40 *
41 * The host firmware has a finite amount of space to store PELs before
42 * sending to the OS, and it's possible it will fill up. In this case,
43 * the AckPEL command will have a special response that will tell the
44 * PLDM daemon to call HostReject D-Bus method on this daemon instead
45 * which will invoke HostNotifier::setHostFull(). This will stop new
46 * PELs from being sent, and the first PEL that hits this will have
47 * a timer set to retry again later.
Matt Spinlerf60ac272019-12-11 13:47:50 -060048 */
49class HostNotifier
50{
51 public:
52 HostNotifier() = delete;
53 HostNotifier(const HostNotifier&) = delete;
54 HostNotifier& operator=(const HostNotifier&) = delete;
55 HostNotifier(HostNotifier&&) = delete;
56 HostNotifier& operator=(HostNotifier&&) = delete;
57
58 /**
59 * @brief Constructor
60 *
61 * @param[in] repo - The PEL repository object
62 * @param[in] dataIface - The data interface object
63 * @param[in] hostIface - The host interface object
64 */
65 HostNotifier(Repository& repo, DataInterfaceBase& dataIface,
66 std::unique_ptr<HostInterface> hostIface);
67
68 /**
69 * @brief Destructor
70 */
71 ~HostNotifier();
72
73 /**
74 * @brief Returns the PEL queue size.
75 *
76 * For testing.
77 *
78 * @return size_t - The queue size
79 */
80 size_t queueSize() const
81 {
82 return _pelQueue.size();
83 }
84
85 /**
86 * @brief Specifies if the PEL needs to go onto the queue to be
87 * set to the host.
88 *
Matt Spinlera943b152019-12-11 14:44:50 -060089 * Only returns false if:
90 * - Already acked by the host (or they didn't like it)
91 * - Hidden and the HMC already got it
92 * - The 'do not report to host' bit is set
93 *
Matt Spinlerf60ac272019-12-11 13:47:50 -060094 * @param[in] id - The PEL ID
95 *
96 * @return bool - If enqueue is required
97 */
98 bool enqueueRequired(uint32_t id) const;
99
Matt Spinlerf77debb2019-12-12 10:04:33 -0600100 /**
101 * @brief If the host still needs to be notified of the PEL
102 * at the time of the notification.
103 *
104 * Only returns false if:
105 * - Already acked by the host
106 * - It's hidden, and the HMC already got or will get it.
107 *
108 * @param[in] id - The PEL ID
109 *
110 * @return bool - If the notify is required
111 */
112 bool notifyRequired(uint32_t id) const;
113
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600114 /**
115 * @brief Called when the host sends the 'ack' PLDM command.
116 *
117 * This means the PEL never needs to be sent up again.
118 *
Matt Spinler41293cb2019-12-12 13:11:09 -0600119 * If the host was previously full, it is also an indication
120 * it no longer is.
121 *
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600122 * @param[in] id - The PEL ID
123 */
124 void ackPEL(uint32_t id);
125
Matt Spinler41293cb2019-12-12 13:11:09 -0600126 /**
127 * @brief Called when the host does not have room for more
128 * PELs at this time.
129 *
130 * This can happen when an OS isn't running yet, and the
131 * staging area to hold the PELs before sending them up
132 * to the OS is full. This will stop future PEls from being
133 * sent up, as explained below.
134 *
135 * The PEL with this ID will need to be sent again, so its
136 * state is set back to 'new', and it is removed from the list
137 * of already sent PELs.
138 *
139 * A timer will be started, if it isn't already running, to
140 * issue another send in the hopes that space has been freed
141 * up by then (Receiving an ackPEL response is also an
142 * indication of this if there happened to have been other
143 * PELs in flight).
144 *
145 * @param[in] id - The PEL ID
146 */
147 void setHostFull(uint32_t id);
148
Matt Spinlera19b6232019-12-12 13:30:14 -0600149 /**
150 * @brief Called when the host receives a malformed PEL.
151 *
152 * Ideally this will never happen, as the Repository
153 * class already purges malformed PELs.
154 *
155 * The PEL should never be sent up again.
156 *
157 * @param[in] id - The PEL ID
158 */
159 void setBadPEL(uint32_t id);
160
Matt Spinlerf60ac272019-12-11 13:47:50 -0600161 private:
162 /**
163 * @brief This function gets called by the Repository class
164 * when a new PEL is added to it.
165 *
Matt Spinler7d800a42019-12-12 10:35:01 -0600166 * This function puts the PEL on the queue to be sent up if it
167 * needs it, and possibly dispatch the send if the conditions call
168 * for it.
169 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600170 * @param[in] pel - The new PEL
171 */
172 void newLogCallback(const PEL& pel);
173
174 /**
Matt Spinler7cb985f2020-03-05 16:02:39 -0600175 * @brief This function gets called by the Repository class
176 * when a PEL is deleted.
177 *
178 * The deleted ID will be removed from the PEL queue and the
179 * sent list.
180 *
181 * @param[in] id - The deleted PEL ID
182 */
183 void deleteLogCallback(uint32_t id);
184
185 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600186 * @brief This function runs on every existing PEL at startup
187 * and puts the PEL on the queue to send if necessary.
188 *
189 * @param[in] pel - The PEL
190 *
191 * @return bool - This is an indicator to the Repository::for_each
192 * function to traverse every PEL. Always false.
193 */
194 bool addPELToQueue(const PEL& pel);
195
196 /**
Matt Spinlerf77debb2019-12-12 10:04:33 -0600197 * @brief Takes the first PEL from the queue that needs to be
198 * sent, and issues the send if conditions are right.
Matt Spinlerf60ac272019-12-11 13:47:50 -0600199 */
200 void doNewLogNotify();
201
202 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600203 * @brief Creates the event object to handle sending the PLDM
204 * command from the event loop.
205 */
206 void scheduleDispatch();
207
208 /**
209 * @brief Kicks off the PLDM send, but called from the event
210 * loop.
211 *
212 * @param[in] source - The event source object
213 */
214 void dispatch(sdeventplus::source::EventBase& source);
215
216 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600217 * @brief Called when the host changes state.
218 *
Matt Spinler3019c6f2019-12-11 15:24:45 -0600219 * If the new state is host up and there are PELs to send, it
220 * will trigger the first command. If the new state is off, then
221 * it will transfer any PELs that were sent but not acked yet back
222 * to the queue to be sent again.
223 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600224 * @param[in] hostUp - The new host state
225 */
226 void hostStateChange(bool hostUp);
227
228 /**
229 * @brief The callback function invoked after the asynchronous
230 * PLDM receive function is complete.
231 *
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600232 * If the command was successful, the state of that PEL will
233 * be set to 'sent', and the next send will be triggered.
234 *
235 * If the command failed, a retry timer will be started so it
236 * can be sent again.
237 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600238 * @param[in] status - The response status
239 */
240 void commandResponse(ResponseStatus status);
241
242 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600243 * @brief The function called when the command failure retry
244 * time is up.
245 *
246 * It will issue a send of the previous PEL and increment the
247 * retry count.
248 */
249 void retryTimerExpired();
250
251 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600252 * @brief The function called when the 'host full' retry timer
253 * expires.
254 *
255 * This will re-issue a command to try again with the PEL at
256 * the front of the queue.
257 */
258 void hostFullTimerExpired();
259
260 /**
Matt Spinlere5f75082022-01-24 16:09:51 -0600261 * @brief The function called when the host up retry timer
262 * expires.
263 *
264 * It kicks off sending the new log commands.
265 */
266 void hostUpTimerExpired();
267
268 /**
Matt Spinler3019c6f2019-12-11 15:24:45 -0600269 * @brief Stops an in progress command
270 *
271 * In progress meaning after the send but before the response.
272 */
273 void stopCommand();
274
275 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600276 * @brief The PEL repository object
277 */
278 Repository& _repo;
279
280 /**
281 * @brief The data interface object
282 */
283 DataInterfaceBase& _dataIface;
284
285 /**
286 * @brief Base class pointer for the host command interface
287 */
288 std::unique_ptr<HostInterface> _hostIface;
289
290 /**
291 * @brief The list of PEL IDs that need to be sent.
292 */
293 std::deque<uint32_t> _pelQueue;
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600294
295 /**
296 * @brief The list of IDs that were sent, but not acked yet.
297 *
298 * These move back to _pelQueue on a power off.
299 */
300 std::vector<uint32_t> _sentPELs;
301
302 /**
303 * @brief The ID the PEL where the notification has
304 * been kicked off but the asynchronous response
305 * hasn't been received yet.
306 */
307 uint32_t _inProgressPEL = 0;
308
309 /**
310 * @brief The command retry count
311 */
312 size_t _retryCount = 0;
313
314 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600315 * @brief Indicates if the host has said it is full and does not
316 * currently have the space for more PELs.
317 */
318 bool _hostFull = false;
319
320 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600321 * @brief The command retry timer.
322 */
323 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
Matt Spinler7d800a42019-12-12 10:35:01 -0600324
325 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600326 * @brief The host full timer, used to retry sending a PEL if the host
327 * said it is full.
328 */
329 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer;
330
331 /**
Matt Spinlere5f75082022-01-24 16:09:51 -0600332 * @brief The host up timer, used to kick off sending commands to the
333 * host after a delay after the host is up.
334 */
335 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostUpTimer;
336
337 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600338 * @brief The object used to dispatch a new PEL send from the
339 * event loop, so the calling function can be returned from
340 * first.
341 */
342 std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
Matt Spinlerf60ac272019-12-11 13:47:50 -0600343};
344
345} // namespace openpower::pels