blob: 971dadde8a2e22d1acb1084a38cc67334b012560 [file] [log] [blame]
Matt Spinlerf60ac272019-12-11 13:47:50 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "extensions/openpower-pels/data_interface.hpp"
17#include "extensions/openpower-pels/host_notifier.hpp"
18#include "mocks.hpp"
19#include "pel_utils.hpp"
20
21#include <fcntl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <chrono>
26
27#include <gtest/gtest.h>
28
29using namespace openpower::pels;
30using ::testing::_;
31using ::testing::Invoke;
Matt Spinler56e08262020-01-27 16:14:23 -060032using ::testing::NiceMock;
Matt Spinlerf60ac272019-12-11 13:47:50 -060033using ::testing::Return;
34namespace fs = std::filesystem;
35using namespace std::chrono;
36
37const size_t actionFlags0Offset = 66;
38const size_t actionFlags1Offset = 67;
39
40class HostNotifierTest : public CleanPELFiles
41{
Matt Spinlera943b152019-12-11 14:44:50 -060042 public:
Matt Spinler56e08262020-01-27 16:14:23 -060043 HostNotifierTest() : repo(repoPath)
Matt Spinlera943b152019-12-11 14:44:50 -060044 {
45 auto r = sd_event_default(&event);
46 EXPECT_TRUE(r >= 0);
Matt Spinler56e08262020-01-27 16:14:23 -060047
Matt Spinler24a85582020-01-27 16:40:21 -060048 ON_CALL(dataIface, getHostPELEnablement).WillByDefault(Return(true));
49
Patrick Williams2544b412022-10-04 08:41:06 -050050 hostIface = std::make_unique<NiceMock<MockHostInterface>>(event,
51 dataIface);
Matt Spinler56e08262020-01-27 16:14:23 -060052
53 mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
54
Patrick Williamsd26fa3e2021-04-21 15:22:23 -050055 auto send = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -060056 return this->mockHostIface->send(0);
57 };
58
59 // Unless otherwise specified, sendNewLogCmd should always pass.
60 ON_CALL(*mockHostIface, sendNewLogCmd(_, _))
61 .WillByDefault(Invoke(send));
Matt Spinlera943b152019-12-11 14:44:50 -060062 }
63
64 ~HostNotifierTest()
65 {
66 sd_event_unref(event);
67 }
68
69 protected:
70 sd_event* event;
Matt Spinler56e08262020-01-27 16:14:23 -060071 Repository repo;
72 NiceMock<MockDataInterface> dataIface;
73 std::unique_ptr<HostInterface> hostIface;
74 MockHostInterface* mockHostIface;
Matt Spinlerf60ac272019-12-11 13:47:50 -060075};
76
77/**
78 * @brief Create PEL with the specified action flags
79 *
80 * @param[in] actionFlagsMask - Optional action flags to use
81 *
82 * @return std::unique_ptr<PEL>
83 */
84std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
85{
86 static uint32_t obmcID = 1;
87 auto data = pelDataFactory(TestPELType::pelSimple);
88
89 data[actionFlags0Offset] |= actionFlagsMask >> 8;
90 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
91
92 auto pel = std::make_unique<PEL>(data, obmcID++);
93 pel->assignID();
94 pel->setCommitTime();
95 return pel;
96}
97
Matt Spinler7d800a42019-12-12 10:35:01 -060098/**
99 * @brief Run an iteration of the event loop.
100 *
101 * An event loop is used for:
102 * 1) timer expiration callbacks
103 * 2) Dispatches
104 * 3) host interface receive callbacks
105 *
106 * @param[in] event - The event object
107 * @param[in] numEvents - number of times to call Event::run()
108 * @param[in] timeout - timeout value for run()
109 */
110void runEvents(sdeventplus::Event& event, size_t numEvents,
111 milliseconds timeout = milliseconds(1))
112{
113 for (size_t i = 0; i < numEvents; i++)
114 {
115 event.run(timeout);
116 }
117}
118
Matt Spinlerf60ac272019-12-11 13:47:50 -0600119// Test that host state change callbacks work
120TEST_F(HostNotifierTest, TestHostStateChange)
121{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600122 bool hostState = false;
123 bool called = false;
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500124 DataInterfaceBase::HostStateChangeFunc func =
125 [&hostState, &called](bool state) {
Matt Spinlerf60ac272019-12-11 13:47:50 -0600126 hostState = state;
127 called = true;
128 };
129
130 dataIface.subscribeToHostStateChange("test", func);
131
132 // callback called
133 dataIface.changeHostState(true);
134 EXPECT_TRUE(called);
135 EXPECT_TRUE(hostState);
136
137 // No change, not called
138 called = false;
139 dataIface.changeHostState(true);
140 EXPECT_FALSE(called);
141
142 // Called again
143 dataIface.changeHostState(false);
144 EXPECT_FALSE(hostState);
145 EXPECT_TRUE(called);
146
147 // Shouldn't get called after an unsubscribe
148 dataIface.unsubscribeFromHostStateChange("test");
149
150 called = false;
151
152 dataIface.changeHostState(true);
153 EXPECT_FALSE(called);
154}
155
Matt Spinlera943b152019-12-11 14:44:50 -0600156// Test dealing with how acked PELs are put on the
157// notification queue.
158TEST_F(HostNotifierTest, TestPolicyAckedPEL)
159{
Matt Spinlera943b152019-12-11 14:44:50 -0600160 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
161
162 auto pel = makePEL();
163 repo.add(pel);
164
165 // This is required
166 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600167 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600168
169 // Not in the repo
170 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600171 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600172
173 // Now set this PEL to host acked
174 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
175
176 // Since it's acked, doesn't need to be enqueued or transmitted
177 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600178 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600179}
180
181// Test the 'don't report' PEL flag
182TEST_F(HostNotifierTest, TestPolicyDontReport)
183{
Matt Spinlera943b152019-12-11 14:44:50 -0600184 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
185
186 // dontReportToHostFlagBit
187 auto pel = makePEL(0x1000);
188
189 // Double check the action flag is still set
190 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
191 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
192
193 repo.add(pel);
194
195 // Don't need to send this to the host
196 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
197}
198
199// Test that hidden PELs need notification when there
200// is no HMC.
201TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
202{
Matt Spinlera943b152019-12-11 14:44:50 -0600203 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
204
205 // hiddenFlagBit
206 auto pel = makePEL(0x4000);
207
208 // Double check the action flag is still set
209 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
210 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
211
212 repo.add(pel);
213
214 // Still need to enqueue this
215 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600216
217 // Still need to send it
218 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600219}
220
221// Don't need to enqueue a hidden log already acked by the HMC
222TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
223{
Matt Spinlera943b152019-12-11 14:44:50 -0600224 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
225
226 // hiddenFlagBit
227 auto pel = makePEL(0x4000);
228
229 // Double check the action flag is still set
230 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
231 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
232
233 repo.add(pel);
234
235 // No HMC yet, so required
236 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
237
238 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
239
240 // Not required anymore
241 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
242}
243
Matt Spinlerf77debb2019-12-12 10:04:33 -0600244// Test that changing the HMC manage status affects
245// the policy with hidden log notification.
246TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
247{
Matt Spinlerf77debb2019-12-12 10:04:33 -0600248 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
249
250 // hiddenFlagBit
251 auto pel = makePEL(0x4000);
252
253 repo.add(pel);
254
255 // The first time, the HMC managed is false
256 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
257
258 dataIface.setHMCManaged(true);
259
260 // This time, HMC managed is true so no need to notify
261 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
262}
263
Matt Spinlerf60ac272019-12-11 13:47:50 -0600264// Test that PELs are enqueued on startup
265TEST_F(HostNotifierTest, TestStartup)
266{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600267 // Give the repo 10 PELs to start with
268 for (int i = 0; i < 10; i++)
269 {
270 auto pel = makePEL();
271 repo.add(pel);
272 }
273
Matt Spinlerf60ac272019-12-11 13:47:50 -0600274 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
275
276 ASSERT_EQ(notifier.queueSize(), 10);
277
278 // Now add 10 more after the notifier is watching
279 for (int i = 0; i < 10; i++)
280 {
281 auto pel = makePEL();
282 repo.add(pel);
283 }
284
285 ASSERT_EQ(notifier.queueSize(), 20);
286}
Matt Spinler7d800a42019-12-12 10:35:01 -0600287
288// Test the simple path were PELs get sent to the host
289TEST_F(HostNotifierTest, TestSendCmd)
290{
Matt Spinler7d800a42019-12-12 10:35:01 -0600291 sdeventplus::Event sdEvent{event};
292
Matt Spinler7d800a42019-12-12 10:35:01 -0600293 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
294
Matt Spinler7d800a42019-12-12 10:35:01 -0600295 // Add a PEL with the host off
296 auto pel = makePEL();
297 repo.add(pel);
298
299 EXPECT_EQ(notifier.queueSize(), 1);
300
301 dataIface.changeHostState(true);
302
Matt Spinlere5f75082022-01-24 16:09:51 -0600303 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600304
305 // It was sent up
Matt Spinler56e08262020-01-27 16:14:23 -0600306 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600307 EXPECT_EQ(notifier.queueSize(), 0);
308
309 // Verify the state was written to the PEL.
310 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
311 auto data = repo.getPELData(id);
312 PEL pelFromRepo{*data};
313 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
314
315 // Add a few more PELs. They will get sent.
316 pel = makePEL();
317 repo.add(pel);
318
319 // Dispatch it by hitting the event loop (no commands sent yet)
320 // Don't need to test this step discretely in the future
321 runEvents(sdEvent, 1);
Matt Spinler56e08262020-01-27 16:14:23 -0600322 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600323 EXPECT_EQ(notifier.queueSize(), 0);
324
325 // Send the command
326 runEvents(sdEvent, 1);
327
Matt Spinler56e08262020-01-27 16:14:23 -0600328 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600329 EXPECT_EQ(notifier.queueSize(), 0);
330
331 pel = makePEL();
332 repo.add(pel);
333
334 // dispatch and process the command
335 runEvents(sdEvent, 2);
336
Matt Spinler56e08262020-01-27 16:14:23 -0600337 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600338 EXPECT_EQ(notifier.queueSize(), 0);
339}
340
341// Test that if the class is created with the host up,
342// it will send PELs
343TEST_F(HostNotifierTest, TestStartAfterHostUp)
344{
Matt Spinler7d800a42019-12-12 10:35:01 -0600345 // Add PELs right away
346 auto pel = makePEL();
347 repo.add(pel);
348 pel = makePEL();
349 repo.add(pel);
350
351 sdeventplus::Event sdEvent{event};
352
Matt Spinler7d800a42019-12-12 10:35:01 -0600353 // Create the HostNotifier class with the host already up
354 dataIface.changeHostState(true);
355 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
356
357 // It should start sending PELs right away
Matt Spinlere5f75082022-01-24 16:09:51 -0600358 runEvents(sdEvent, 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600359
Matt Spinler56e08262020-01-27 16:14:23 -0600360 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600361 EXPECT_EQ(notifier.queueSize(), 0);
362}
363
364// Test that a single failure will cause a retry
365TEST_F(HostNotifierTest, TestHostRetry)
366{
Matt Spinler7d800a42019-12-12 10:35:01 -0600367 sdeventplus::Event sdEvent{event};
368
Matt Spinler7d800a42019-12-12 10:35:01 -0600369 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
370
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500371 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600372 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600373 };
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500374 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600375 return this->mockHostIface->send(0);
Matt Spinler7d800a42019-12-12 10:35:01 -0600376 };
377
Matt Spinler56e08262020-01-27 16:14:23 -0600378 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinler7d800a42019-12-12 10:35:01 -0600379 .WillOnce(Invoke(sendFailure))
380 .WillOnce(Invoke(sendSuccess))
381 .WillOnce(Invoke(sendSuccess));
382
383 dataIface.changeHostState(true);
384
385 auto pel = makePEL();
386 repo.add(pel);
387
388 // Dispatch and handle the command
389 runEvents(sdEvent, 2);
390
391 // The command failed, so the queue isn't empty
Matt Spinler56e08262020-01-27 16:14:23 -0600392 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600393 EXPECT_EQ(notifier.queueSize(), 1);
394
395 // Run the events again to let the timer expire and the
396 // command to be retried, which will be successful.
Matt Spinler56e08262020-01-27 16:14:23 -0600397 runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600398
Matt Spinler56e08262020-01-27 16:14:23 -0600399 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600400 EXPECT_EQ(notifier.queueSize(), 0);
401
402 // This one should pass with no problems
403 pel = makePEL();
404 repo.add(pel);
405
406 // Dispatch and handle the command
407 runEvents(sdEvent, 2);
408
Matt Spinler56e08262020-01-27 16:14:23 -0600409 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600410 EXPECT_EQ(notifier.queueSize(), 0);
411}
412
413// Test that all commands fail and notifier will give up
414TEST_F(HostNotifierTest, TestHardFailure)
415{
Matt Spinler7d800a42019-12-12 10:35:01 -0600416 sdeventplus::Event sdEvent{event};
417
Matt Spinler7d800a42019-12-12 10:35:01 -0600418 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
419
420 // Every call will fail
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500421 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600422 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600423 };
424
Matt Spinler56e08262020-01-27 16:14:23 -0600425 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinler7d800a42019-12-12 10:35:01 -0600426 .WillRepeatedly(Invoke(sendFailure));
427
428 dataIface.changeHostState(true);
429
430 auto pel = makePEL();
431 repo.add(pel);
432
433 // Clock more retries than necessary
Matt Spinler56e08262020-01-27 16:14:23 -0600434 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600435
436 // Should have stopped after the 15 Tries
Matt Spinler56e08262020-01-27 16:14:23 -0600437 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
Matt Spinler7d800a42019-12-12 10:35:01 -0600438 EXPECT_EQ(notifier.queueSize(), 1);
439
440 // Now add another PEL, and it should start trying again
441 // though it will also eventually give up
442 pel = makePEL();
443 repo.add(pel);
444
Matt Spinler56e08262020-01-27 16:14:23 -0600445 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600446
447 // Tried an additional 15 times
Matt Spinler56e08262020-01-27 16:14:23 -0600448 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
Matt Spinler7d800a42019-12-12 10:35:01 -0600449 EXPECT_EQ(notifier.queueSize(), 2);
450}
451
Matt Spinler527ff342023-06-29 12:52:34 -0500452// Test that if the command cannot be started it will give
453// up but still try again later
454TEST_F(HostNotifierTest, TestCannotStartCmd)
455{
456 sdeventplus::Event sdEvent{event};
457
458 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
459
460 // Make it behave like startCommand() fails.
461 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
462 this->mockHostIface->cancelCmd();
463 return CmdStatus::failure;
464 };
465
466 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
467 return this->mockHostIface->send(0);
468 };
469
470 // Fails 16 times (1 fail + 15 retries) and
471 // then start working.
472 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
473 .WillOnce(Invoke(sendFailure))
474 .WillOnce(Invoke(sendFailure))
475 .WillOnce(Invoke(sendFailure))
476 .WillOnce(Invoke(sendFailure))
477 .WillOnce(Invoke(sendFailure))
478 .WillOnce(Invoke(sendFailure))
479 .WillOnce(Invoke(sendFailure))
480 .WillOnce(Invoke(sendFailure))
481 .WillOnce(Invoke(sendFailure))
482 .WillOnce(Invoke(sendFailure))
483 .WillOnce(Invoke(sendFailure))
484 .WillOnce(Invoke(sendFailure))
485 .WillOnce(Invoke(sendFailure))
486 .WillOnce(Invoke(sendFailure))
487 .WillOnce(Invoke(sendFailure))
488 .WillOnce(Invoke(sendFailure))
489 .WillRepeatedly(Invoke(sendSuccess));
490
491 dataIface.changeHostState(true);
492
493 auto pel = makePEL();
494 repo.add(pel);
495
496 // Clock more retries than necessary
497 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
498
499 // Didn't get far enough for a cmd to be processed
500 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 0);
501 EXPECT_EQ(notifier.queueSize(), 1);
502
503 // At this point, commands will work again.
504
505 pel = makePEL();
506 repo.add(pel);
507
508 // Run the events to send the PELs
509 runEvents(sdEvent, 5, mockHostIface->getReceiveRetryDelay());
510
511 // All PELs sent
512 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
513 EXPECT_EQ(notifier.queueSize(), 0);
514}
515
Matt Spinler7d800a42019-12-12 10:35:01 -0600516// Cancel an in progress command
517TEST_F(HostNotifierTest, TestCancelCmd)
518{
Matt Spinler7d800a42019-12-12 10:35:01 -0600519 sdeventplus::Event sdEvent{event};
Matt Spinler7d800a42019-12-12 10:35:01 -0600520 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
521
Matt Spinler7d800a42019-12-12 10:35:01 -0600522 dataIface.changeHostState(true);
523
524 // Add and send one PEL, but don't enter the event loop
525 // so the receive function can't run.
526 auto pel = makePEL();
527 repo.add(pel);
528
529 // Not dispatched yet
530 EXPECT_EQ(notifier.queueSize(), 1);
531
532 // Dispatch it
Matt Spinlere5f75082022-01-24 16:09:51 -0600533 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600534
535 // It was sent and off the queue
536 EXPECT_EQ(notifier.queueSize(), 0);
537
538 // This will cancel the receive
539 dataIface.changeHostState(false);
540
541 // Back on the queue
542 EXPECT_EQ(notifier.queueSize(), 1);
543
544 // Turn the host back on and make sure
545 // commands will work again
546 dataIface.changeHostState(true);
547
548 runEvents(sdEvent, 1);
549
Matt Spinler56e08262020-01-27 16:14:23 -0600550 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600551 EXPECT_EQ(notifier.queueSize(), 0);
552}
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600553
554// Test that acking a PEL persist across power cycles
555TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
556{
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600557 sdeventplus::Event sdEvent{event};
558
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600559 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
560
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600561 // Add 2 PELs with host off
562 auto pel = makePEL();
563 repo.add(pel);
564 auto id1 = pel->id();
565
566 pel = makePEL();
567 repo.add(pel);
568 auto id2 = pel->id();
569
570 dataIface.changeHostState(true);
571
Matt Spinlere5f75082022-01-24 16:09:51 -0600572 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600573
574 // The were both sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600575 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600576 EXPECT_EQ(notifier.queueSize(), 0);
577
578 dataIface.changeHostState(false);
579
580 // Those PELs weren't acked, so they will get sent again
581 EXPECT_EQ(notifier.queueSize(), 2);
582
583 // Power back on and send them again
584 dataIface.changeHostState(true);
Matt Spinlere5f75082022-01-24 16:09:51 -0600585 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600586
Matt Spinler56e08262020-01-27 16:14:23 -0600587 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600588 EXPECT_EQ(notifier.queueSize(), 0);
589
590 // Ack them and verify the state in the PEL.
591 notifier.ackPEL(id1);
592 notifier.ackPEL(id2);
593
594 Repository::LogID id{Repository::LogID::Pel{id1}};
595 auto data = repo.getPELData(id);
596 PEL pelFromRepo1{*data};
597 EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
598
599 id.pelID.id = id2;
600 data = repo.getPELData(id);
601 PEL pelFromRepo2{*data};
602 EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
603
604 // Power back off, and they should't get re-added
605 dataIface.changeHostState(false);
606
607 EXPECT_EQ(notifier.queueSize(), 0);
608}
Matt Spinler41293cb2019-12-12 13:11:09 -0600609
610// Test the host full condition
611TEST_F(HostNotifierTest, TestHostFull)
612{
613 // The full interaction with the host is:
614 // BMC: new PEL available
615 // Host: ReadPELFile (not modeled here)
616 // Host: Ack(id) (if not full), or HostFull(id)
617 // BMC: if full and any new PELs come in, don't sent them
618 // Start a timer and try again
619 // Host responds with either Ack or full
620 // and repeat
621
Matt Spinler41293cb2019-12-12 13:11:09 -0600622 sdeventplus::Event sdEvent{event};
Matt Spinler41293cb2019-12-12 13:11:09 -0600623 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
624
Matt Spinler41293cb2019-12-12 13:11:09 -0600625 dataIface.changeHostState(true);
626
627 // Add and dispatch/send one PEL
628 auto pel = makePEL();
629 auto id = pel->id();
630 repo.add(pel);
631 runEvents(sdEvent, 2);
632
Matt Spinler56e08262020-01-27 16:14:23 -0600633 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600634 EXPECT_EQ(notifier.queueSize(), 0);
635
636 // Host is full
637 notifier.setHostFull(id);
638
639 // It goes back on the queue
640 EXPECT_EQ(notifier.queueSize(), 1);
641
642 // The transmission state goes back to new
643 Repository::LogID i{Repository::LogID::Pel{id}};
644 auto data = repo.getPELData(i);
645 PEL pelFromRepo{*data};
646 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
647
648 // Clock it, nothing should be sent still.
649 runEvents(sdEvent, 1);
650
Matt Spinler56e08262020-01-27 16:14:23 -0600651 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600652 EXPECT_EQ(notifier.queueSize(), 1);
653
654 // Add another PEL and clock it, still nothing sent
655 pel = makePEL();
656 repo.add(pel);
657 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600658 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600659 EXPECT_EQ(notifier.queueSize(), 2);
660
661 // Let the host full timer expire to trigger a retry.
662 // Add some extra event passes just to be sure nothing new is sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600663 runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
Matt Spinler41293cb2019-12-12 13:11:09 -0600664
665 // The timer expiration will send just the 1, not both
Matt Spinler56e08262020-01-27 16:14:23 -0600666 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler41293cb2019-12-12 13:11:09 -0600667 EXPECT_EQ(notifier.queueSize(), 1);
668
669 // Host still full
670 notifier.setHostFull(id);
671
672 // Let the host full timer attempt again
Matt Spinler56e08262020-01-27 16:14:23 -0600673 runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
674 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600675
676 // Add yet another PEL with the retry timer expired.
677 // It shouldn't get sent out.
678 pel = makePEL();
679 repo.add(pel);
680 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600681 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600682
683 // The last 2 PELs still on the queue
684 EXPECT_EQ(notifier.queueSize(), 2);
685
686 // Host no longer full, it finally acks the first PEL
687 notifier.ackPEL(id);
688
689 // Now the remaining 2 PELs will be dispatched
690 runEvents(sdEvent, 3);
691
Matt Spinler56e08262020-01-27 16:14:23 -0600692 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
Matt Spinler41293cb2019-12-12 13:11:09 -0600693 EXPECT_EQ(notifier.queueSize(), 0);
694}
Matt Spinlera19b6232019-12-12 13:30:14 -0600695
696// Test when the host says it was send a malformed PEL
697TEST_F(HostNotifierTest, TestBadPEL)
698{
Matt Spinlera19b6232019-12-12 13:30:14 -0600699 sdeventplus::Event sdEvent{event};
700
701 {
Matt Spinler56e08262020-01-27 16:14:23 -0600702 Repository repo1{repoPath};
703 HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600704
705 dataIface.changeHostState(true);
706
707 // Add a PEL and dispatch and send it
708 auto pel = makePEL();
709 auto id = pel->id();
Matt Spinler56e08262020-01-27 16:14:23 -0600710 repo1.add(pel);
Matt Spinlera19b6232019-12-12 13:30:14 -0600711
712 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600713 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinlera19b6232019-12-12 13:30:14 -0600714 EXPECT_EQ(notifier.queueSize(), 0);
715
716 // The host rejected it.
717 notifier.setBadPEL(id);
718
719 // Doesn't go back on the queue
720 EXPECT_EQ(notifier.queueSize(), 0);
721
722 // Check the state was saved in the PEL itself
723 Repository::LogID i{Repository::LogID::Pel{id}};
Matt Spinler56e08262020-01-27 16:14:23 -0600724 auto data = repo1.getPELData(i);
Matt Spinlera19b6232019-12-12 13:30:14 -0600725 PEL pelFromRepo{*data};
726 EXPECT_EQ(pelFromRepo.hostTransmissionState(),
727 TransmissionState::badPEL);
728
729 dataIface.changeHostState(false);
730
731 // Ensure it doesn't go back on the queue on a power cycle
732 EXPECT_EQ(notifier.queueSize(), 0);
733 }
734
735 // Now restore the repo, and make sure it doesn't come back
736 {
Matt Spinler56e08262020-01-27 16:14:23 -0600737 Repository repo1{repoPath};
Matt Spinlera19b6232019-12-12 13:30:14 -0600738
Matt Spinler56e08262020-01-27 16:14:23 -0600739 std::unique_ptr<HostInterface> hostIface1 =
Matt Spinlera19b6232019-12-12 13:30:14 -0600740 std::make_unique<MockHostInterface>(event, dataIface);
741
Matt Spinler56e08262020-01-27 16:14:23 -0600742 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600743
744 EXPECT_EQ(notifier.queueSize(), 0);
745 }
746}
Matt Spinler24a85582020-01-27 16:40:21 -0600747
748// Test that sending PELs can be disabled
749TEST_F(HostNotifierTest, TestDisable)
750{
751 // Turn off sending the PELs except for once in the middle
752 EXPECT_CALL(dataIface, getHostPELEnablement())
753 .WillOnce(Return(false))
754 .WillOnce(Return(false))
755 .WillOnce(Return(true))
756 .WillOnce(Return(false))
757 .WillOnce(Return(false))
758 .WillOnce(Return(false));
759
760 {
761 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
762
763 // Add a PEL with the host off
764 auto pel = makePEL();
765 repo.add(pel);
766
767 // Not added to the send queue
768 EXPECT_EQ(notifier.queueSize(), 0);
769
770 dataIface.changeHostState(true);
771
772 // Try again with the host on
773 pel = makePEL();
774 repo.add(pel);
775
776 EXPECT_EQ(notifier.queueSize(), 0);
777
778 // Now getHostPELEnablement() will return true for the new PEL
779 pel = makePEL();
780 repo.add(pel);
781
782 EXPECT_EQ(notifier.queueSize(), 1);
783 }
784
785 // getHostPELEnablement is back to returning false.
786 // Create a new second instance and make sure the 3 existing PELs
787 // aren't put on the queue on startup
788 {
789 Repository repo1{repoPath};
790 std::unique_ptr<HostInterface> hostIface1 =
791 std::make_unique<MockHostInterface>(event, dataIface);
792
793 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
794
795 EXPECT_EQ(notifier.queueSize(), 0);
796 }
797}