blob: 017b1ee61b01f7a42a6812656ea6f5807f67a106 [file] [log] [blame]
Alexander Hansen40fb5492025-10-28 17:56:12 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
Matt Spinlerf60ac272019-12-11 13:47:50 -06004#include "extensions/openpower-pels/data_interface.hpp"
5#include "extensions/openpower-pels/host_notifier.hpp"
6#include "mocks.hpp"
7#include "pel_utils.hpp"
8
9#include <fcntl.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12
13#include <chrono>
14
15#include <gtest/gtest.h>
16
17using namespace openpower::pels;
18using ::testing::_;
19using ::testing::Invoke;
Matt Spinler56e08262020-01-27 16:14:23 -060020using ::testing::NiceMock;
Matt Spinlerf60ac272019-12-11 13:47:50 -060021using ::testing::Return;
22namespace fs = std::filesystem;
23using namespace std::chrono;
24
25const size_t actionFlags0Offset = 66;
26const size_t actionFlags1Offset = 67;
27
28class HostNotifierTest : public CleanPELFiles
29{
Matt Spinlera943b152019-12-11 14:44:50 -060030 public:
Matt Spinler56e08262020-01-27 16:14:23 -060031 HostNotifierTest() : repo(repoPath)
Matt Spinlera943b152019-12-11 14:44:50 -060032 {
33 auto r = sd_event_default(&event);
34 EXPECT_TRUE(r >= 0);
Matt Spinler56e08262020-01-27 16:14:23 -060035
Matt Spinler24a85582020-01-27 16:40:21 -060036 ON_CALL(dataIface, getHostPELEnablement).WillByDefault(Return(true));
37
Patrick Williams075c7922024-08-16 15:19:49 -040038 hostIface =
39 std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
Matt Spinler56e08262020-01-27 16:14:23 -060040
41 mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
42
Patrick Williamsd26fa3e2021-04-21 15:22:23 -050043 auto send = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -060044 return this->mockHostIface->send(0);
45 };
46
47 // Unless otherwise specified, sendNewLogCmd should always pass.
Matt Spinlerd438c092025-10-24 10:00:02 -050048 ON_CALL(*mockHostIface, sendNewLogCmd(_, _)).WillByDefault(send);
Matt Spinlera943b152019-12-11 14:44:50 -060049 }
50
51 ~HostNotifierTest()
52 {
53 sd_event_unref(event);
54 }
55
56 protected:
57 sd_event* event;
Matt Spinler56e08262020-01-27 16:14:23 -060058 Repository repo;
59 NiceMock<MockDataInterface> dataIface;
60 std::unique_ptr<HostInterface> hostIface;
61 MockHostInterface* mockHostIface;
Matt Spinlerf60ac272019-12-11 13:47:50 -060062};
63
64/**
65 * @brief Create PEL with the specified action flags
66 *
67 * @param[in] actionFlagsMask - Optional action flags to use
68 *
69 * @return std::unique_ptr<PEL>
70 */
71std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
72{
73 static uint32_t obmcID = 1;
74 auto data = pelDataFactory(TestPELType::pelSimple);
75
76 data[actionFlags0Offset] |= actionFlagsMask >> 8;
77 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
78
79 auto pel = std::make_unique<PEL>(data, obmcID++);
80 pel->assignID();
81 pel->setCommitTime();
82 return pel;
83}
84
Matt Spinler7d800a42019-12-12 10:35:01 -060085/**
86 * @brief Run an iteration of the event loop.
87 *
88 * An event loop is used for:
89 * 1) timer expiration callbacks
90 * 2) Dispatches
91 * 3) host interface receive callbacks
92 *
93 * @param[in] event - The event object
94 * @param[in] numEvents - number of times to call Event::run()
95 * @param[in] timeout - timeout value for run()
96 */
97void runEvents(sdeventplus::Event& event, size_t numEvents,
98 milliseconds timeout = milliseconds(1))
99{
100 for (size_t i = 0; i < numEvents; i++)
101 {
102 event.run(timeout);
103 }
104}
105
Matt Spinlerf60ac272019-12-11 13:47:50 -0600106// Test that host state change callbacks work
107TEST_F(HostNotifierTest, TestHostStateChange)
108{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600109 bool hostState = false;
110 bool called = false;
Patrick Williams075c7922024-08-16 15:19:49 -0400111 DataInterfaceBase::HostStateChangeFunc func =
112 [&hostState, &called](bool state) {
113 hostState = state;
114 called = true;
115 };
Matt Spinlerf60ac272019-12-11 13:47:50 -0600116
117 dataIface.subscribeToHostStateChange("test", func);
118
119 // callback called
120 dataIface.changeHostState(true);
121 EXPECT_TRUE(called);
122 EXPECT_TRUE(hostState);
123
124 // No change, not called
125 called = false;
126 dataIface.changeHostState(true);
127 EXPECT_FALSE(called);
128
129 // Called again
130 dataIface.changeHostState(false);
131 EXPECT_FALSE(hostState);
132 EXPECT_TRUE(called);
133
134 // Shouldn't get called after an unsubscribe
135 dataIface.unsubscribeFromHostStateChange("test");
136
137 called = false;
138
139 dataIface.changeHostState(true);
140 EXPECT_FALSE(called);
141}
142
Matt Spinlera943b152019-12-11 14:44:50 -0600143// Test dealing with how acked PELs are put on the
144// notification queue.
145TEST_F(HostNotifierTest, TestPolicyAckedPEL)
146{
Matt Spinlera943b152019-12-11 14:44:50 -0600147 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
148
149 auto pel = makePEL();
150 repo.add(pel);
151
152 // This is required
153 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600154 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600155
156 // Not in the repo
157 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600158 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600159
160 // Now set this PEL to host acked
161 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
162
163 // Since it's acked, doesn't need to be enqueued or transmitted
164 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600165 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600166}
167
168// Test the 'don't report' PEL flag
169TEST_F(HostNotifierTest, TestPolicyDontReport)
170{
Matt Spinlera943b152019-12-11 14:44:50 -0600171 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
172
173 // dontReportToHostFlagBit
174 auto pel = makePEL(0x1000);
175
176 // Double check the action flag is still set
177 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
178 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
179
180 repo.add(pel);
181
182 // Don't need to send this to the host
183 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
184}
185
186// Test that hidden PELs need notification when there
187// is no HMC.
188TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
189{
Matt Spinlera943b152019-12-11 14:44:50 -0600190 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
191
192 // hiddenFlagBit
193 auto pel = makePEL(0x4000);
194
195 // Double check the action flag is still set
196 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
197 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
198
199 repo.add(pel);
200
201 // Still need to enqueue this
202 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600203
204 // Still need to send it
205 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600206}
207
208// Don't need to enqueue a hidden log already acked by the HMC
209TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
210{
Matt Spinlera943b152019-12-11 14:44:50 -0600211 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
212
213 // hiddenFlagBit
214 auto pel = makePEL(0x4000);
215
216 // Double check the action flag is still set
217 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
218 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
219
220 repo.add(pel);
221
222 // No HMC yet, so required
223 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
224
225 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
226
227 // Not required anymore
228 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
229}
230
Matt Spinlerf77debb2019-12-12 10:04:33 -0600231// Test that changing the HMC manage status affects
232// the policy with hidden log notification.
233TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
234{
Matt Spinlerf77debb2019-12-12 10:04:33 -0600235 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
236
237 // hiddenFlagBit
238 auto pel = makePEL(0x4000);
239
240 repo.add(pel);
241
242 // The first time, the HMC managed is false
243 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
244
245 dataIface.setHMCManaged(true);
246
247 // This time, HMC managed is true so no need to notify
248 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
249}
250
Matt Spinlerf60ac272019-12-11 13:47:50 -0600251// Test that PELs are enqueued on startup
252TEST_F(HostNotifierTest, TestStartup)
253{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600254 // Give the repo 10 PELs to start with
255 for (int i = 0; i < 10; i++)
256 {
257 auto pel = makePEL();
258 repo.add(pel);
259 }
260
Matt Spinlerf60ac272019-12-11 13:47:50 -0600261 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
262
263 ASSERT_EQ(notifier.queueSize(), 10);
264
265 // Now add 10 more after the notifier is watching
266 for (int i = 0; i < 10; i++)
267 {
268 auto pel = makePEL();
269 repo.add(pel);
270 }
271
272 ASSERT_EQ(notifier.queueSize(), 20);
273}
Matt Spinler7d800a42019-12-12 10:35:01 -0600274
275// Test the simple path were PELs get sent to the host
276TEST_F(HostNotifierTest, TestSendCmd)
277{
Matt Spinler7d800a42019-12-12 10:35:01 -0600278 sdeventplus::Event sdEvent{event};
279
Matt Spinler7d800a42019-12-12 10:35:01 -0600280 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
281
Matt Spinler7d800a42019-12-12 10:35:01 -0600282 // Add a PEL with the host off
283 auto pel = makePEL();
284 repo.add(pel);
285
286 EXPECT_EQ(notifier.queueSize(), 1);
287
288 dataIface.changeHostState(true);
289
Matt Spinlere5f75082022-01-24 16:09:51 -0600290 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600291
292 // It was sent up
Matt Spinler56e08262020-01-27 16:14:23 -0600293 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600294 EXPECT_EQ(notifier.queueSize(), 0);
295
296 // Verify the state was written to the PEL.
297 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
298 auto data = repo.getPELData(id);
299 PEL pelFromRepo{*data};
300 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
301
302 // Add a few more PELs. They will get sent.
303 pel = makePEL();
304 repo.add(pel);
305
306 // Dispatch it by hitting the event loop (no commands sent yet)
307 // Don't need to test this step discretely in the future
308 runEvents(sdEvent, 1);
Matt Spinler56e08262020-01-27 16:14:23 -0600309 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600310 EXPECT_EQ(notifier.queueSize(), 0);
311
312 // Send the command
313 runEvents(sdEvent, 1);
314
Matt Spinler56e08262020-01-27 16:14:23 -0600315 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600316 EXPECT_EQ(notifier.queueSize(), 0);
317
318 pel = makePEL();
319 repo.add(pel);
320
321 // dispatch and process the command
322 runEvents(sdEvent, 2);
323
Matt Spinler56e08262020-01-27 16:14:23 -0600324 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600325 EXPECT_EQ(notifier.queueSize(), 0);
326}
327
328// Test that if the class is created with the host up,
329// it will send PELs
330TEST_F(HostNotifierTest, TestStartAfterHostUp)
331{
Matt Spinler7d800a42019-12-12 10:35:01 -0600332 // Add PELs right away
333 auto pel = makePEL();
334 repo.add(pel);
335 pel = makePEL();
336 repo.add(pel);
337
338 sdeventplus::Event sdEvent{event};
339
Matt Spinler7d800a42019-12-12 10:35:01 -0600340 // Create the HostNotifier class with the host already up
341 dataIface.changeHostState(true);
342 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
343
344 // It should start sending PELs right away
Matt Spinlere5f75082022-01-24 16:09:51 -0600345 runEvents(sdEvent, 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600346
Matt Spinler56e08262020-01-27 16:14:23 -0600347 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600348 EXPECT_EQ(notifier.queueSize(), 0);
349}
350
351// Test that a single failure will cause a retry
352TEST_F(HostNotifierTest, TestHostRetry)
353{
Matt Spinler7d800a42019-12-12 10:35:01 -0600354 sdeventplus::Event sdEvent{event};
355
Matt Spinler7d800a42019-12-12 10:35:01 -0600356 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
357
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500358 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600359 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600360 };
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500361 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600362 return this->mockHostIface->send(0);
Matt Spinler7d800a42019-12-12 10:35:01 -0600363 };
364
Matt Spinler56e08262020-01-27 16:14:23 -0600365 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinlerd438c092025-10-24 10:00:02 -0500366 .WillOnce(sendFailure)
367 .WillOnce(sendSuccess)
368 .WillOnce(sendSuccess);
Matt Spinler7d800a42019-12-12 10:35:01 -0600369
370 dataIface.changeHostState(true);
371
372 auto pel = makePEL();
373 repo.add(pel);
374
375 // Dispatch and handle the command
376 runEvents(sdEvent, 2);
377
378 // The command failed, so the queue isn't empty
Matt Spinler56e08262020-01-27 16:14:23 -0600379 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600380 EXPECT_EQ(notifier.queueSize(), 1);
381
382 // Run the events again to let the timer expire and the
383 // command to be retried, which will be successful.
Matt Spinler56e08262020-01-27 16:14:23 -0600384 runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600385
Matt Spinler56e08262020-01-27 16:14:23 -0600386 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600387 EXPECT_EQ(notifier.queueSize(), 0);
388
389 // This one should pass with no problems
390 pel = makePEL();
391 repo.add(pel);
392
393 // Dispatch and handle the command
394 runEvents(sdEvent, 2);
395
Matt Spinler56e08262020-01-27 16:14:23 -0600396 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600397 EXPECT_EQ(notifier.queueSize(), 0);
398}
399
400// Test that all commands fail and notifier will give up
401TEST_F(HostNotifierTest, TestHardFailure)
402{
Matt Spinler7d800a42019-12-12 10:35:01 -0600403 sdeventplus::Event sdEvent{event};
404
Matt Spinler7d800a42019-12-12 10:35:01 -0600405 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
406
407 // Every call will fail
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500408 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600409 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600410 };
411
Matt Spinler56e08262020-01-27 16:14:23 -0600412 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinlerd438c092025-10-24 10:00:02 -0500413 .WillRepeatedly(sendFailure);
Matt Spinler7d800a42019-12-12 10:35:01 -0600414
415 dataIface.changeHostState(true);
416
417 auto pel = makePEL();
418 repo.add(pel);
419
420 // Clock more retries than necessary
Matt Spinler56e08262020-01-27 16:14:23 -0600421 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600422
423 // Should have stopped after the 15 Tries
Matt Spinler56e08262020-01-27 16:14:23 -0600424 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
Matt Spinler7d800a42019-12-12 10:35:01 -0600425 EXPECT_EQ(notifier.queueSize(), 1);
426
427 // Now add another PEL, and it should start trying again
428 // though it will also eventually give up
429 pel = makePEL();
430 repo.add(pel);
431
Matt Spinler56e08262020-01-27 16:14:23 -0600432 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600433
434 // Tried an additional 15 times
Matt Spinler56e08262020-01-27 16:14:23 -0600435 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
Matt Spinler7d800a42019-12-12 10:35:01 -0600436 EXPECT_EQ(notifier.queueSize(), 2);
437}
438
Matt Spinler527ff342023-06-29 12:52:34 -0500439// Test that if the command cannot be started it will give
440// up but still try again later
441TEST_F(HostNotifierTest, TestCannotStartCmd)
442{
443 sdeventplus::Event sdEvent{event};
444
445 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
446
447 // Make it behave like startCommand() fails.
448 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
449 this->mockHostIface->cancelCmd();
450 return CmdStatus::failure;
451 };
452
453 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
454 return this->mockHostIface->send(0);
455 };
456
457 // Fails 16 times (1 fail + 15 retries) and
458 // then start working.
459 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinlerd438c092025-10-24 10:00:02 -0500460 .WillOnce(sendFailure)
461 .WillOnce(sendFailure)
462 .WillOnce(sendFailure)
463 .WillOnce(sendFailure)
464 .WillOnce(sendFailure)
465 .WillOnce(sendFailure)
466 .WillOnce(sendFailure)
467 .WillOnce(sendFailure)
468 .WillOnce(sendFailure)
469 .WillOnce(sendFailure)
470 .WillOnce(sendFailure)
471 .WillOnce(sendFailure)
472 .WillOnce(sendFailure)
473 .WillOnce(sendFailure)
474 .WillOnce(sendFailure)
475 .WillOnce(sendFailure)
476 .WillRepeatedly(sendSuccess);
Matt Spinler527ff342023-06-29 12:52:34 -0500477
478 dataIface.changeHostState(true);
479
480 auto pel = makePEL();
481 repo.add(pel);
482
483 // Clock more retries than necessary
484 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
485
486 // Didn't get far enough for a cmd to be processed
487 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 0);
488 EXPECT_EQ(notifier.queueSize(), 1);
489
490 // At this point, commands will work again.
491
492 pel = makePEL();
493 repo.add(pel);
494
495 // Run the events to send the PELs
496 runEvents(sdEvent, 5, mockHostIface->getReceiveRetryDelay());
497
498 // All PELs sent
499 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
500 EXPECT_EQ(notifier.queueSize(), 0);
501}
502
Matt Spinler7d800a42019-12-12 10:35:01 -0600503// Cancel an in progress command
504TEST_F(HostNotifierTest, TestCancelCmd)
505{
Matt Spinler7d800a42019-12-12 10:35:01 -0600506 sdeventplus::Event sdEvent{event};
Matt Spinler7d800a42019-12-12 10:35:01 -0600507 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
508
Matt Spinler7d800a42019-12-12 10:35:01 -0600509 dataIface.changeHostState(true);
510
511 // Add and send one PEL, but don't enter the event loop
512 // so the receive function can't run.
513 auto pel = makePEL();
514 repo.add(pel);
515
516 // Not dispatched yet
517 EXPECT_EQ(notifier.queueSize(), 1);
518
519 // Dispatch it
Matt Spinlere5f75082022-01-24 16:09:51 -0600520 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600521
522 // It was sent and off the queue
523 EXPECT_EQ(notifier.queueSize(), 0);
524
525 // This will cancel the receive
526 dataIface.changeHostState(false);
527
528 // Back on the queue
529 EXPECT_EQ(notifier.queueSize(), 1);
530
531 // Turn the host back on and make sure
532 // commands will work again
533 dataIface.changeHostState(true);
534
535 runEvents(sdEvent, 1);
536
Matt Spinler56e08262020-01-27 16:14:23 -0600537 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600538 EXPECT_EQ(notifier.queueSize(), 0);
539}
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600540
541// Test that acking a PEL persist across power cycles
542TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
543{
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600544 sdeventplus::Event sdEvent{event};
545
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600546 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
547
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600548 // Add 2 PELs with host off
549 auto pel = makePEL();
550 repo.add(pel);
551 auto id1 = pel->id();
552
553 pel = makePEL();
554 repo.add(pel);
555 auto id2 = pel->id();
556
557 dataIface.changeHostState(true);
558
Matt Spinlere5f75082022-01-24 16:09:51 -0600559 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600560
561 // The were both sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600562 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600563 EXPECT_EQ(notifier.queueSize(), 0);
564
565 dataIface.changeHostState(false);
566
567 // Those PELs weren't acked, so they will get sent again
568 EXPECT_EQ(notifier.queueSize(), 2);
569
570 // Power back on and send them again
571 dataIface.changeHostState(true);
Matt Spinlere5f75082022-01-24 16:09:51 -0600572 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600573
Matt Spinler56e08262020-01-27 16:14:23 -0600574 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600575 EXPECT_EQ(notifier.queueSize(), 0);
576
577 // Ack them and verify the state in the PEL.
578 notifier.ackPEL(id1);
579 notifier.ackPEL(id2);
580
581 Repository::LogID id{Repository::LogID::Pel{id1}};
582 auto data = repo.getPELData(id);
583 PEL pelFromRepo1{*data};
584 EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
585
586 id.pelID.id = id2;
587 data = repo.getPELData(id);
588 PEL pelFromRepo2{*data};
589 EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
590
591 // Power back off, and they should't get re-added
592 dataIface.changeHostState(false);
593
594 EXPECT_EQ(notifier.queueSize(), 0);
595}
Matt Spinler41293cb2019-12-12 13:11:09 -0600596
597// Test the host full condition
598TEST_F(HostNotifierTest, TestHostFull)
599{
600 // The full interaction with the host is:
601 // BMC: new PEL available
602 // Host: ReadPELFile (not modeled here)
603 // Host: Ack(id) (if not full), or HostFull(id)
604 // BMC: if full and any new PELs come in, don't sent them
605 // Start a timer and try again
606 // Host responds with either Ack or full
607 // and repeat
608
Matt Spinler41293cb2019-12-12 13:11:09 -0600609 sdeventplus::Event sdEvent{event};
Matt Spinler41293cb2019-12-12 13:11:09 -0600610 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
611
Matt Spinler41293cb2019-12-12 13:11:09 -0600612 dataIface.changeHostState(true);
613
614 // Add and dispatch/send one PEL
615 auto pel = makePEL();
616 auto id = pel->id();
617 repo.add(pel);
618 runEvents(sdEvent, 2);
619
Matt Spinler56e08262020-01-27 16:14:23 -0600620 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600621 EXPECT_EQ(notifier.queueSize(), 0);
622
623 // Host is full
624 notifier.setHostFull(id);
625
626 // It goes back on the queue
627 EXPECT_EQ(notifier.queueSize(), 1);
628
629 // The transmission state goes back to new
630 Repository::LogID i{Repository::LogID::Pel{id}};
631 auto data = repo.getPELData(i);
632 PEL pelFromRepo{*data};
633 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
634
635 // Clock it, nothing should be sent still.
636 runEvents(sdEvent, 1);
637
Matt Spinler56e08262020-01-27 16:14:23 -0600638 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600639 EXPECT_EQ(notifier.queueSize(), 1);
640
641 // Add another PEL and clock it, still nothing sent
642 pel = makePEL();
643 repo.add(pel);
644 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600645 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600646 EXPECT_EQ(notifier.queueSize(), 2);
647
648 // Let the host full timer expire to trigger a retry.
649 // Add some extra event passes just to be sure nothing new is sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600650 runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
Matt Spinler41293cb2019-12-12 13:11:09 -0600651
652 // The timer expiration will send just the 1, not both
Matt Spinler56e08262020-01-27 16:14:23 -0600653 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler41293cb2019-12-12 13:11:09 -0600654 EXPECT_EQ(notifier.queueSize(), 1);
655
656 // Host still full
657 notifier.setHostFull(id);
658
659 // Let the host full timer attempt again
Matt Spinler56e08262020-01-27 16:14:23 -0600660 runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
661 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600662
663 // Add yet another PEL with the retry timer expired.
664 // It shouldn't get sent out.
665 pel = makePEL();
666 repo.add(pel);
667 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600668 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600669
670 // The last 2 PELs still on the queue
671 EXPECT_EQ(notifier.queueSize(), 2);
672
673 // Host no longer full, it finally acks the first PEL
674 notifier.ackPEL(id);
675
676 // Now the remaining 2 PELs will be dispatched
677 runEvents(sdEvent, 3);
678
Matt Spinler56e08262020-01-27 16:14:23 -0600679 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
Matt Spinler41293cb2019-12-12 13:11:09 -0600680 EXPECT_EQ(notifier.queueSize(), 0);
681}
Matt Spinlera19b6232019-12-12 13:30:14 -0600682
683// Test when the host says it was send a malformed PEL
684TEST_F(HostNotifierTest, TestBadPEL)
685{
Matt Spinlera19b6232019-12-12 13:30:14 -0600686 sdeventplus::Event sdEvent{event};
687
688 {
Matt Spinler56e08262020-01-27 16:14:23 -0600689 Repository repo1{repoPath};
690 HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600691
692 dataIface.changeHostState(true);
693
694 // Add a PEL and dispatch and send it
695 auto pel = makePEL();
696 auto id = pel->id();
Matt Spinler56e08262020-01-27 16:14:23 -0600697 repo1.add(pel);
Matt Spinlera19b6232019-12-12 13:30:14 -0600698
699 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600700 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinlera19b6232019-12-12 13:30:14 -0600701 EXPECT_EQ(notifier.queueSize(), 0);
702
703 // The host rejected it.
704 notifier.setBadPEL(id);
705
706 // Doesn't go back on the queue
707 EXPECT_EQ(notifier.queueSize(), 0);
708
709 // Check the state was saved in the PEL itself
710 Repository::LogID i{Repository::LogID::Pel{id}};
Matt Spinler56e08262020-01-27 16:14:23 -0600711 auto data = repo1.getPELData(i);
Matt Spinlera19b6232019-12-12 13:30:14 -0600712 PEL pelFromRepo{*data};
713 EXPECT_EQ(pelFromRepo.hostTransmissionState(),
714 TransmissionState::badPEL);
715
716 dataIface.changeHostState(false);
717
718 // Ensure it doesn't go back on the queue on a power cycle
719 EXPECT_EQ(notifier.queueSize(), 0);
720 }
721
722 // Now restore the repo, and make sure it doesn't come back
723 {
Matt Spinler56e08262020-01-27 16:14:23 -0600724 Repository repo1{repoPath};
Matt Spinlera19b6232019-12-12 13:30:14 -0600725
Matt Spinler56e08262020-01-27 16:14:23 -0600726 std::unique_ptr<HostInterface> hostIface1 =
Matt Spinlera19b6232019-12-12 13:30:14 -0600727 std::make_unique<MockHostInterface>(event, dataIface);
728
Matt Spinler56e08262020-01-27 16:14:23 -0600729 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600730
731 EXPECT_EQ(notifier.queueSize(), 0);
732 }
733}
Matt Spinler24a85582020-01-27 16:40:21 -0600734
735// Test that sending PELs can be disabled
736TEST_F(HostNotifierTest, TestDisable)
737{
738 // Turn off sending the PELs except for once in the middle
739 EXPECT_CALL(dataIface, getHostPELEnablement())
740 .WillOnce(Return(false))
741 .WillOnce(Return(false))
742 .WillOnce(Return(true))
743 .WillOnce(Return(false))
744 .WillOnce(Return(false))
745 .WillOnce(Return(false));
746
747 {
748 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
749
750 // Add a PEL with the host off
751 auto pel = makePEL();
752 repo.add(pel);
753
754 // Not added to the send queue
755 EXPECT_EQ(notifier.queueSize(), 0);
756
757 dataIface.changeHostState(true);
758
759 // Try again with the host on
760 pel = makePEL();
761 repo.add(pel);
762
763 EXPECT_EQ(notifier.queueSize(), 0);
764
765 // Now getHostPELEnablement() will return true for the new PEL
766 pel = makePEL();
767 repo.add(pel);
768
769 EXPECT_EQ(notifier.queueSize(), 1);
770 }
771
772 // getHostPELEnablement is back to returning false.
773 // Create a new second instance and make sure the 3 existing PELs
774 // aren't put on the queue on startup
775 {
776 Repository repo1{repoPath};
777 std::unique_ptr<HostInterface> hostIface1 =
778 std::make_unique<MockHostInterface>(event, dataIface);
779
780 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
781
782 EXPECT_EQ(notifier.queueSize(), 0);
783 }
784}