blob: 0c907e0b8b65cdb87490fcbd075e619bfee215a6 [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
7#include <deque>
Matt Spinlerf869fcf2019-12-11 15:02:20 -06008#include <sdeventplus/clock.hpp>
Matt Spinler7d800a42019-12-12 10:35:01 -06009#include <sdeventplus/source/event.hpp>
Matt Spinlerf869fcf2019-12-11 15:02:20 -060010#include <sdeventplus/utility/timer.hpp>
Matt Spinlerf60ac272019-12-11 13:47:50 -060011
12namespace openpower::pels
13{
14
15/**
16 * @class HostNotifier
17 *
18 * This class handles notifying the host firmware of new PELs.
Matt Spinler0e1593e2019-12-16 10:50:43 -060019 *
20 * It uses the Repository class's subscription feature to be
21 * notified about new PELs.
22 *
23 * Some PELs do not need to be sent - see enqueueRequired() and
24 * notifyRequired().
25 *
26 * The high level good path flow for sending a single PEL is:
27 *
28 * 1) Send the ID and size of the new PEL to the host.
29 * - The command response is asynchronous.
30 *
31 * 2) The host reads the raw PEL data (outside of this class).
32 *
33 * 3) The host sends the PEL to the OS, and then sends an AckPEL
34 * PLDM command to the PLDM daemon, who makes a D-Bus method
35 * call to this daemon, which calls HostNotifer::ackPEL().
36 *
37 * After this, a PEL never needs to be sent again, but if the
38 * host is rebooted before the ack comes it will.
39 *
40 * The host firmware has a finite amount of space to store PELs before
41 * sending to the OS, and it's possible it will fill up. In this case,
42 * the AckPEL command will have a special response that will tell the
43 * PLDM daemon to call HostReject D-Bus method on this daemon instead
44 * which will invoke HostNotifier::setHostFull(). This will stop new
45 * PELs from being sent, and the first PEL that hits this will have
46 * a timer set to retry again later.
Matt Spinlerf60ac272019-12-11 13:47:50 -060047 */
48class HostNotifier
49{
50 public:
51 HostNotifier() = delete;
52 HostNotifier(const HostNotifier&) = delete;
53 HostNotifier& operator=(const HostNotifier&) = delete;
54 HostNotifier(HostNotifier&&) = delete;
55 HostNotifier& operator=(HostNotifier&&) = delete;
56
57 /**
58 * @brief Constructor
59 *
60 * @param[in] repo - The PEL repository object
61 * @param[in] dataIface - The data interface object
62 * @param[in] hostIface - The host interface object
63 */
64 HostNotifier(Repository& repo, DataInterfaceBase& dataIface,
65 std::unique_ptr<HostInterface> hostIface);
66
67 /**
68 * @brief Destructor
69 */
70 ~HostNotifier();
71
72 /**
73 * @brief Returns the PEL queue size.
74 *
75 * For testing.
76 *
77 * @return size_t - The queue size
78 */
79 size_t queueSize() const
80 {
81 return _pelQueue.size();
82 }
83
84 /**
85 * @brief Specifies if the PEL needs to go onto the queue to be
86 * set to the host.
87 *
Matt Spinlera943b152019-12-11 14:44:50 -060088 * Only returns false if:
89 * - Already acked by the host (or they didn't like it)
90 * - Hidden and the HMC already got it
91 * - The 'do not report to host' bit is set
92 *
Matt Spinlerf60ac272019-12-11 13:47:50 -060093 * @param[in] id - The PEL ID
94 *
95 * @return bool - If enqueue is required
96 */
97 bool enqueueRequired(uint32_t id) const;
98
Matt Spinlerf77debb2019-12-12 10:04:33 -060099 /**
100 * @brief If the host still needs to be notified of the PEL
101 * at the time of the notification.
102 *
103 * Only returns false if:
104 * - Already acked by the host
105 * - It's hidden, and the HMC already got or will get it.
106 *
107 * @param[in] id - The PEL ID
108 *
109 * @return bool - If the notify is required
110 */
111 bool notifyRequired(uint32_t id) const;
112
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600113 /**
114 * @brief Called when the host sends the 'ack' PLDM command.
115 *
116 * This means the PEL never needs to be sent up again.
117 *
Matt Spinler41293cb2019-12-12 13:11:09 -0600118 * If the host was previously full, it is also an indication
119 * it no longer is.
120 *
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600121 * @param[in] id - The PEL ID
122 */
123 void ackPEL(uint32_t id);
124
Matt Spinler41293cb2019-12-12 13:11:09 -0600125 /**
126 * @brief Called when the host does not have room for more
127 * PELs at this time.
128 *
129 * This can happen when an OS isn't running yet, and the
130 * staging area to hold the PELs before sending them up
131 * to the OS is full. This will stop future PEls from being
132 * sent up, as explained below.
133 *
134 * The PEL with this ID will need to be sent again, so its
135 * state is set back to 'new', and it is removed from the list
136 * of already sent PELs.
137 *
138 * A timer will be started, if it isn't already running, to
139 * issue another send in the hopes that space has been freed
140 * up by then (Receiving an ackPEL response is also an
141 * indication of this if there happened to have been other
142 * PELs in flight).
143 *
144 * @param[in] id - The PEL ID
145 */
146 void setHostFull(uint32_t id);
147
Matt Spinlera19b6232019-12-12 13:30:14 -0600148 /**
149 * @brief Called when the host receives a malformed PEL.
150 *
151 * Ideally this will never happen, as the Repository
152 * class already purges malformed PELs.
153 *
154 * The PEL should never be sent up again.
155 *
156 * @param[in] id - The PEL ID
157 */
158 void setBadPEL(uint32_t id);
159
Matt Spinlerf60ac272019-12-11 13:47:50 -0600160 private:
161 /**
162 * @brief This function gets called by the Repository class
163 * when a new PEL is added to it.
164 *
Matt Spinler7d800a42019-12-12 10:35:01 -0600165 * This function puts the PEL on the queue to be sent up if it
166 * needs it, and possibly dispatch the send if the conditions call
167 * for it.
168 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600169 * @param[in] pel - The new PEL
170 */
171 void newLogCallback(const PEL& pel);
172
173 /**
Matt Spinler7cb985f2020-03-05 16:02:39 -0600174 * @brief This function gets called by the Repository class
175 * when a PEL is deleted.
176 *
177 * The deleted ID will be removed from the PEL queue and the
178 * sent list.
179 *
180 * @param[in] id - The deleted PEL ID
181 */
182 void deleteLogCallback(uint32_t id);
183
184 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600185 * @brief This function runs on every existing PEL at startup
186 * and puts the PEL on the queue to send if necessary.
187 *
188 * @param[in] pel - The PEL
189 *
190 * @return bool - This is an indicator to the Repository::for_each
191 * function to traverse every PEL. Always false.
192 */
193 bool addPELToQueue(const PEL& pel);
194
195 /**
Matt Spinlerf77debb2019-12-12 10:04:33 -0600196 * @brief Takes the first PEL from the queue that needs to be
197 * sent, and issues the send if conditions are right.
Matt Spinlerf60ac272019-12-11 13:47:50 -0600198 */
199 void doNewLogNotify();
200
201 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600202 * @brief Creates the event object to handle sending the PLDM
203 * command from the event loop.
204 */
205 void scheduleDispatch();
206
207 /**
208 * @brief Kicks off the PLDM send, but called from the event
209 * loop.
210 *
211 * @param[in] source - The event source object
212 */
213 void dispatch(sdeventplus::source::EventBase& source);
214
215 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600216 * @brief Called when the host changes state.
217 *
Matt Spinler3019c6f2019-12-11 15:24:45 -0600218 * If the new state is host up and there are PELs to send, it
219 * will trigger the first command. If the new state is off, then
220 * it will transfer any PELs that were sent but not acked yet back
221 * to the queue to be sent again.
222 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600223 * @param[in] hostUp - The new host state
224 */
225 void hostStateChange(bool hostUp);
226
227 /**
228 * @brief The callback function invoked after the asynchronous
229 * PLDM receive function is complete.
230 *
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600231 * If the command was successful, the state of that PEL will
232 * be set to 'sent', and the next send will be triggered.
233 *
234 * If the command failed, a retry timer will be started so it
235 * can be sent again.
236 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600237 * @param[in] status - The response status
238 */
239 void commandResponse(ResponseStatus status);
240
241 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600242 * @brief The function called when the command failure retry
243 * time is up.
244 *
245 * It will issue a send of the previous PEL and increment the
246 * retry count.
247 */
248 void retryTimerExpired();
249
250 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600251 * @brief The function called when the 'host full' retry timer
252 * expires.
253 *
254 * This will re-issue a command to try again with the PEL at
255 * the front of the queue.
256 */
257 void hostFullTimerExpired();
258
259 /**
Matt Spinlere5f75082022-01-24 16:09:51 -0600260 * @brief The function called when the host up retry timer
261 * expires.
262 *
263 * It kicks off sending the new log commands.
264 */
265 void hostUpTimerExpired();
266
267 /**
Matt Spinler3019c6f2019-12-11 15:24:45 -0600268 * @brief Stops an in progress command
269 *
270 * In progress meaning after the send but before the response.
271 */
272 void stopCommand();
273
274 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600275 * @brief The PEL repository object
276 */
277 Repository& _repo;
278
279 /**
280 * @brief The data interface object
281 */
282 DataInterfaceBase& _dataIface;
283
284 /**
285 * @brief Base class pointer for the host command interface
286 */
287 std::unique_ptr<HostInterface> _hostIface;
288
289 /**
290 * @brief The list of PEL IDs that need to be sent.
291 */
292 std::deque<uint32_t> _pelQueue;
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600293
294 /**
295 * @brief The list of IDs that were sent, but not acked yet.
296 *
297 * These move back to _pelQueue on a power off.
298 */
299 std::vector<uint32_t> _sentPELs;
300
301 /**
302 * @brief The ID the PEL where the notification has
303 * been kicked off but the asynchronous response
304 * hasn't been received yet.
305 */
306 uint32_t _inProgressPEL = 0;
307
308 /**
309 * @brief The command retry count
310 */
311 size_t _retryCount = 0;
312
313 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600314 * @brief Indicates if the host has said it is full and does not
315 * currently have the space for more PELs.
316 */
317 bool _hostFull = false;
318
319 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600320 * @brief The command retry timer.
321 */
322 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
Matt Spinler7d800a42019-12-12 10:35:01 -0600323
324 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600325 * @brief The host full timer, used to retry sending a PEL if the host
326 * said it is full.
327 */
328 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer;
329
330 /**
Matt Spinlere5f75082022-01-24 16:09:51 -0600331 * @brief The host up timer, used to kick off sending commands to the
332 * host after a delay after the host is up.
333 */
334 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostUpTimer;
335
336 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600337 * @brief The object used to dispatch a new PEL send from the
338 * event loop, so the calling function can be returned from
339 * first.
340 */
341 std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
Matt Spinlerf60ac272019-12-11 13:47:50 -0600342};
343
344} // namespace openpower::pels