blob: 9bab0308625a9c879a54adb163cbb3dea3e5c23b [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;
32using ::testing::Return;
33namespace fs = std::filesystem;
34using namespace std::chrono;
35
36const size_t actionFlags0Offset = 66;
37const size_t actionFlags1Offset = 67;
38
39class HostNotifierTest : public CleanPELFiles
40{
Matt Spinlera943b152019-12-11 14:44:50 -060041 public:
42 HostNotifierTest()
43 {
44 auto r = sd_event_default(&event);
45 EXPECT_TRUE(r >= 0);
46 }
47
48 ~HostNotifierTest()
49 {
50 sd_event_unref(event);
51 }
52
53 protected:
54 sd_event* event;
Matt Spinlerf60ac272019-12-11 13:47:50 -060055};
56
57/**
58 * @brief Create PEL with the specified action flags
59 *
60 * @param[in] actionFlagsMask - Optional action flags to use
61 *
62 * @return std::unique_ptr<PEL>
63 */
64std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
65{
66 static uint32_t obmcID = 1;
67 auto data = pelDataFactory(TestPELType::pelSimple);
68
69 data[actionFlags0Offset] |= actionFlagsMask >> 8;
70 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
71
72 auto pel = std::make_unique<PEL>(data, obmcID++);
73 pel->assignID();
74 pel->setCommitTime();
75 return pel;
76}
77
Matt Spinler7d800a42019-12-12 10:35:01 -060078/**
79 * @brief Run an iteration of the event loop.
80 *
81 * An event loop is used for:
82 * 1) timer expiration callbacks
83 * 2) Dispatches
84 * 3) host interface receive callbacks
85 *
86 * @param[in] event - The event object
87 * @param[in] numEvents - number of times to call Event::run()
88 * @param[in] timeout - timeout value for run()
89 */
90void runEvents(sdeventplus::Event& event, size_t numEvents,
91 milliseconds timeout = milliseconds(1))
92{
93 for (size_t i = 0; i < numEvents; i++)
94 {
95 event.run(timeout);
96 }
97}
98
Matt Spinlerf60ac272019-12-11 13:47:50 -060099// Test that host state change callbacks work
100TEST_F(HostNotifierTest, TestHostStateChange)
101{
102 MockDataInterface dataIface;
103
104 bool hostState = false;
105 bool called = false;
106 DataInterfaceBase::HostStateChangeFunc func = [&hostState,
107 &called](bool state) {
108 hostState = state;
109 called = true;
110 };
111
112 dataIface.subscribeToHostStateChange("test", func);
113
114 // callback called
115 dataIface.changeHostState(true);
116 EXPECT_TRUE(called);
117 EXPECT_TRUE(hostState);
118
119 // No change, not called
120 called = false;
121 dataIface.changeHostState(true);
122 EXPECT_FALSE(called);
123
124 // Called again
125 dataIface.changeHostState(false);
126 EXPECT_FALSE(hostState);
127 EXPECT_TRUE(called);
128
129 // Shouldn't get called after an unsubscribe
130 dataIface.unsubscribeFromHostStateChange("test");
131
132 called = false;
133
134 dataIface.changeHostState(true);
135 EXPECT_FALSE(called);
136}
137
Matt Spinlera943b152019-12-11 14:44:50 -0600138// Test dealing with how acked PELs are put on the
139// notification queue.
140TEST_F(HostNotifierTest, TestPolicyAckedPEL)
141{
142 Repository repo{repoPath};
143 MockDataInterface dataIface;
144
145 std::unique_ptr<HostInterface> hostIface =
146 std::make_unique<MockHostInterface>(event, dataIface);
147
148 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
149
150 auto pel = makePEL();
151 repo.add(pel);
152
153 // This is required
154 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600155 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600156
157 // Not in the repo
158 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600159 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600160
161 // Now set this PEL to host acked
162 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
163
164 // Since it's acked, doesn't need to be enqueued or transmitted
165 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600166 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600167}
168
169// Test the 'don't report' PEL flag
170TEST_F(HostNotifierTest, TestPolicyDontReport)
171{
172 Repository repo{repoPath};
173 MockDataInterface dataIface;
174
175 std::unique_ptr<HostInterface> hostIface =
176 std::make_unique<MockHostInterface>(event, dataIface);
177
178 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
179
180 // dontReportToHostFlagBit
181 auto pel = makePEL(0x1000);
182
183 // Double check the action flag is still set
184 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
185 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
186
187 repo.add(pel);
188
189 // Don't need to send this to the host
190 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
191}
192
193// Test that hidden PELs need notification when there
194// is no HMC.
195TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
196{
197 Repository repo{repoPath};
198 MockDataInterface dataIface;
199
200 std::unique_ptr<HostInterface> hostIface =
201 std::make_unique<MockHostInterface>(event, dataIface);
202
203 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{
224 Repository repo{repoPath};
225 MockDataInterface dataIface;
226
227 std::unique_ptr<HostInterface> hostIface =
228 std::make_unique<MockHostInterface>(event, dataIface);
229
230 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
231
232 // hiddenFlagBit
233 auto pel = makePEL(0x4000);
234
235 // Double check the action flag is still set
236 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
237 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
238
239 repo.add(pel);
240
241 // No HMC yet, so required
242 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
243
244 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
245
246 // Not required anymore
247 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
248}
249
Matt Spinlerf77debb2019-12-12 10:04:33 -0600250// Test that changing the HMC manage status affects
251// the policy with hidden log notification.
252TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
253{
254 Repository repo{repoPath};
255 MockDataInterface dataIface;
256
257 std::unique_ptr<HostInterface> hostIface =
258 std::make_unique<MockHostInterface>(event, dataIface);
259
260 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
261
262 // hiddenFlagBit
263 auto pel = makePEL(0x4000);
264
265 repo.add(pel);
266
267 // The first time, the HMC managed is false
268 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
269
270 dataIface.setHMCManaged(true);
271
272 // This time, HMC managed is true so no need to notify
273 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
274}
275
Matt Spinlerf60ac272019-12-11 13:47:50 -0600276// Test that PELs are enqueued on startup
277TEST_F(HostNotifierTest, TestStartup)
278{
279 Repository repo{repoPath};
280 MockDataInterface dataIface;
281
282 // Give the repo 10 PELs to start with
283 for (int i = 0; i < 10; i++)
284 {
285 auto pel = makePEL();
286 repo.add(pel);
287 }
288
Matt Spinlerf60ac272019-12-11 13:47:50 -0600289 std::unique_ptr<HostInterface> hostIface =
290 std::make_unique<MockHostInterface>(event, dataIface);
291
292 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
293
294 ASSERT_EQ(notifier.queueSize(), 10);
295
296 // Now add 10 more after the notifier is watching
297 for (int i = 0; i < 10; i++)
298 {
299 auto pel = makePEL();
300 repo.add(pel);
301 }
302
303 ASSERT_EQ(notifier.queueSize(), 20);
304}
Matt Spinler7d800a42019-12-12 10:35:01 -0600305
306// Test the simple path were PELs get sent to the host
307TEST_F(HostNotifierTest, TestSendCmd)
308{
309 Repository repo{repoPath};
310 MockDataInterface dataIface;
311
312 sdeventplus::Event sdEvent{event};
313
314 std::unique_ptr<HostInterface> hostIface =
315 std::make_unique<MockHostInterface>(event, dataIface);
316
317 MockHostInterface& mockHostIface =
318 reinterpret_cast<MockHostInterface&>(*hostIface);
319
320 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
321
322 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
323 return mockHostIface.send(0);
324 };
325
326 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
327 .WillRepeatedly(Invoke(send));
328
329 // Add a PEL with the host off
330 auto pel = makePEL();
331 repo.add(pel);
332
333 EXPECT_EQ(notifier.queueSize(), 1);
334
335 dataIface.changeHostState(true);
336
337 runEvents(sdEvent, 1);
338
339 // It was sent up
340 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
341 EXPECT_EQ(notifier.queueSize(), 0);
342
343 // Verify the state was written to the PEL.
344 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
345 auto data = repo.getPELData(id);
346 PEL pelFromRepo{*data};
347 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
348
349 // Add a few more PELs. They will get sent.
350 pel = makePEL();
351 repo.add(pel);
352
353 // Dispatch it by hitting the event loop (no commands sent yet)
354 // Don't need to test this step discretely in the future
355 runEvents(sdEvent, 1);
356 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
357 EXPECT_EQ(notifier.queueSize(), 0);
358
359 // Send the command
360 runEvents(sdEvent, 1);
361
362 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
363 EXPECT_EQ(notifier.queueSize(), 0);
364
365 pel = makePEL();
366 repo.add(pel);
367
368 // dispatch and process the command
369 runEvents(sdEvent, 2);
370
371 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3);
372 EXPECT_EQ(notifier.queueSize(), 0);
373}
374
375// Test that if the class is created with the host up,
376// it will send PELs
377TEST_F(HostNotifierTest, TestStartAfterHostUp)
378{
379 Repository repo{repoPath};
380 MockDataInterface dataIface;
381
382 // Add PELs right away
383 auto pel = makePEL();
384 repo.add(pel);
385 pel = makePEL();
386 repo.add(pel);
387
388 sdeventplus::Event sdEvent{event};
389
390 std::unique_ptr<HostInterface> hostIface =
391 std::make_unique<MockHostInterface>(event, dataIface);
392
393 MockHostInterface& mockHostIface =
394 reinterpret_cast<MockHostInterface&>(*hostIface);
395
396 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
397 return mockHostIface.send(0);
398 };
399
400 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
401 .WillRepeatedly(Invoke(send));
402
403 // Create the HostNotifier class with the host already up
404 dataIface.changeHostState(true);
405 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
406
407 // It should start sending PELs right away
408 runEvents(sdEvent, 2);
409
410 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
411 EXPECT_EQ(notifier.queueSize(), 0);
412}
413
414// Test that a single failure will cause a retry
415TEST_F(HostNotifierTest, TestHostRetry)
416{
417 Repository repo{repoPath};
418 MockDataInterface dataIface;
419
420 sdeventplus::Event sdEvent{event};
421
422 std::unique_ptr<HostInterface> hostIface =
423 std::make_unique<MockHostInterface>(event, dataIface);
424
425 MockHostInterface& mockHostIface =
426 reinterpret_cast<MockHostInterface&>(*hostIface);
427
428 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
429
430 auto sendFailure = [&mockHostIface](uint32_t id, uint32_t size) {
431 return mockHostIface.send(1);
432 };
433 auto sendSuccess = [&mockHostIface](uint32_t id, uint32_t size) {
434 return mockHostIface.send(0);
435 };
436
437 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
438 .WillOnce(Invoke(sendFailure))
439 .WillOnce(Invoke(sendSuccess))
440 .WillOnce(Invoke(sendSuccess));
441
442 dataIface.changeHostState(true);
443
444 auto pel = makePEL();
445 repo.add(pel);
446
447 // Dispatch and handle the command
448 runEvents(sdEvent, 2);
449
450 // The command failed, so the queue isn't empty
451 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
452 EXPECT_EQ(notifier.queueSize(), 1);
453
454 // Run the events again to let the timer expire and the
455 // command to be retried, which will be successful.
456 runEvents(sdEvent, 2, mockHostIface.getReceiveRetryDelay());
457
458 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
459 EXPECT_EQ(notifier.queueSize(), 0);
460
461 // This one should pass with no problems
462 pel = makePEL();
463 repo.add(pel);
464
465 // Dispatch and handle the command
466 runEvents(sdEvent, 2);
467
468 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3);
469 EXPECT_EQ(notifier.queueSize(), 0);
470}
471
472// Test that all commands fail and notifier will give up
473TEST_F(HostNotifierTest, TestHardFailure)
474{
475 Repository repo{repoPath};
476 MockDataInterface dataIface;
477
478 sdeventplus::Event sdEvent{event};
479
480 std::unique_ptr<HostInterface> hostIface =
481 std::make_unique<MockHostInterface>(event, dataIface);
482
483 MockHostInterface& mockHostIface =
484 reinterpret_cast<MockHostInterface&>(*hostIface);
485
486 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
487
488 // Every call will fail
489 auto sendFailure = [&mockHostIface](uint32_t id, uint32_t size) {
490 return mockHostIface.send(1);
491 };
492
493 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
494 .WillRepeatedly(Invoke(sendFailure));
495
496 dataIface.changeHostState(true);
497
498 auto pel = makePEL();
499 repo.add(pel);
500
501 // Clock more retries than necessary
502 runEvents(sdEvent, 40, mockHostIface.getReceiveRetryDelay());
503
504 // Should have stopped after the 15 Tries
505 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 15);
506 EXPECT_EQ(notifier.queueSize(), 1);
507
508 // Now add another PEL, and it should start trying again
509 // though it will also eventually give up
510 pel = makePEL();
511 repo.add(pel);
512
513 runEvents(sdEvent, 40, mockHostIface.getReceiveRetryDelay());
514
515 // Tried an additional 15 times
516 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 30);
517 EXPECT_EQ(notifier.queueSize(), 2);
518}
519
520// Cancel an in progress command
521TEST_F(HostNotifierTest, TestCancelCmd)
522{
523 Repository repo{repoPath};
524 MockDataInterface dataIface;
525
526 sdeventplus::Event sdEvent{event};
527
528 std::unique_ptr<HostInterface> hostIface =
529 std::make_unique<MockHostInterface>(event, dataIface);
530
531 MockHostInterface& mockHostIface =
532 reinterpret_cast<MockHostInterface&>(*hostIface);
533
534 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
535
536 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
537 return mockHostIface.send(0);
538 };
539
540 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
541 .WillRepeatedly(Invoke(send));
542
543 dataIface.changeHostState(true);
544
545 // Add and send one PEL, but don't enter the event loop
546 // so the receive function can't run.
547 auto pel = makePEL();
548 repo.add(pel);
549
550 // Not dispatched yet
551 EXPECT_EQ(notifier.queueSize(), 1);
552
553 // Dispatch it
554 runEvents(sdEvent, 1);
555
556 // It was sent and off the queue
557 EXPECT_EQ(notifier.queueSize(), 0);
558
559 // This will cancel the receive
560 dataIface.changeHostState(false);
561
562 // Back on the queue
563 EXPECT_EQ(notifier.queueSize(), 1);
564
565 // Turn the host back on and make sure
566 // commands will work again
567 dataIface.changeHostState(true);
568
569 runEvents(sdEvent, 1);
570
571 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
572 EXPECT_EQ(notifier.queueSize(), 0);
573}
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600574
575// Test that acking a PEL persist across power cycles
576TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
577{
578 Repository repo{repoPath};
579 MockDataInterface dataIface;
580
581 sdeventplus::Event sdEvent{event};
582
583 std::unique_ptr<HostInterface> hostIface =
584 std::make_unique<MockHostInterface>(event, dataIface);
585
586 MockHostInterface& mockHostIface =
587 reinterpret_cast<MockHostInterface&>(*hostIface);
588
589 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
590
591 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
592 return mockHostIface.send(0);
593 };
594
595 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
596 .WillRepeatedly(Invoke(send));
597
598 // Add 2 PELs with host off
599 auto pel = makePEL();
600 repo.add(pel);
601 auto id1 = pel->id();
602
603 pel = makePEL();
604 repo.add(pel);
605 auto id2 = pel->id();
606
607 dataIface.changeHostState(true);
608
609 runEvents(sdEvent, 2);
610
611 // The were both sent.
612 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
613 EXPECT_EQ(notifier.queueSize(), 0);
614
615 dataIface.changeHostState(false);
616
617 // Those PELs weren't acked, so they will get sent again
618 EXPECT_EQ(notifier.queueSize(), 2);
619
620 // Power back on and send them again
621 dataIface.changeHostState(true);
622 runEvents(sdEvent, 2);
623
624 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 4);
625 EXPECT_EQ(notifier.queueSize(), 0);
626
627 // Ack them and verify the state in the PEL.
628 notifier.ackPEL(id1);
629 notifier.ackPEL(id2);
630
631 Repository::LogID id{Repository::LogID::Pel{id1}};
632 auto data = repo.getPELData(id);
633 PEL pelFromRepo1{*data};
634 EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
635
636 id.pelID.id = id2;
637 data = repo.getPELData(id);
638 PEL pelFromRepo2{*data};
639 EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
640
641 // Power back off, and they should't get re-added
642 dataIface.changeHostState(false);
643
644 EXPECT_EQ(notifier.queueSize(), 0);
645}
Matt Spinler41293cb2019-12-12 13:11:09 -0600646
647// Test the host full condition
648TEST_F(HostNotifierTest, TestHostFull)
649{
650 // The full interaction with the host is:
651 // BMC: new PEL available
652 // Host: ReadPELFile (not modeled here)
653 // Host: Ack(id) (if not full), or HostFull(id)
654 // BMC: if full and any new PELs come in, don't sent them
655 // Start a timer and try again
656 // Host responds with either Ack or full
657 // and repeat
658
659 Repository repo{repoPath};
660 MockDataInterface dataIface;
661
662 sdeventplus::Event sdEvent{event};
663
664 std::unique_ptr<HostInterface> hostIface =
665 std::make_unique<MockHostInterface>(event, dataIface);
666
667 MockHostInterface& mockHostIface =
668 reinterpret_cast<MockHostInterface&>(*hostIface);
669
670 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
671
672 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
673 return mockHostIface.send(0);
674 };
675
676 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
677 .WillRepeatedly(Invoke(send));
678
679 dataIface.changeHostState(true);
680
681 // Add and dispatch/send one PEL
682 auto pel = makePEL();
683 auto id = pel->id();
684 repo.add(pel);
685 runEvents(sdEvent, 2);
686
687 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
688 EXPECT_EQ(notifier.queueSize(), 0);
689
690 // Host is full
691 notifier.setHostFull(id);
692
693 // It goes back on the queue
694 EXPECT_EQ(notifier.queueSize(), 1);
695
696 // The transmission state goes back to new
697 Repository::LogID i{Repository::LogID::Pel{id}};
698 auto data = repo.getPELData(i);
699 PEL pelFromRepo{*data};
700 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
701
702 // Clock it, nothing should be sent still.
703 runEvents(sdEvent, 1);
704
705 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
706 EXPECT_EQ(notifier.queueSize(), 1);
707
708 // Add another PEL and clock it, still nothing sent
709 pel = makePEL();
710 repo.add(pel);
711 runEvents(sdEvent, 2);
712 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
713 EXPECT_EQ(notifier.queueSize(), 2);
714
715 // Let the host full timer expire to trigger a retry.
716 // Add some extra event passes just to be sure nothing new is sent.
717 runEvents(sdEvent, 5, mockHostIface.getHostFullRetryDelay());
718
719 // The timer expiration will send just the 1, not both
720 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
721 EXPECT_EQ(notifier.queueSize(), 1);
722
723 // Host still full
724 notifier.setHostFull(id);
725
726 // Let the host full timer attempt again
727 runEvents(sdEvent, 2, mockHostIface.getHostFullRetryDelay());
728 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3);
729
730 // Add yet another PEL with the retry timer expired.
731 // It shouldn't get sent out.
732 pel = makePEL();
733 repo.add(pel);
734 runEvents(sdEvent, 2);
735 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3);
736
737 // The last 2 PELs still on the queue
738 EXPECT_EQ(notifier.queueSize(), 2);
739
740 // Host no longer full, it finally acks the first PEL
741 notifier.ackPEL(id);
742
743 // Now the remaining 2 PELs will be dispatched
744 runEvents(sdEvent, 3);
745
746 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 5);
747 EXPECT_EQ(notifier.queueSize(), 0);
748}
Matt Spinlera19b6232019-12-12 13:30:14 -0600749
750// Test when the host says it was send a malformed PEL
751TEST_F(HostNotifierTest, TestBadPEL)
752{
753 MockDataInterface dataIface;
754 sdeventplus::Event sdEvent{event};
755
756 {
757 Repository repo{repoPath};
758
759 std::unique_ptr<HostInterface> hostIface =
760 std::make_unique<MockHostInterface>(event, dataIface);
761
762 MockHostInterface& mockHostIface =
763 reinterpret_cast<MockHostInterface&>(*hostIface);
764
765 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
766
767 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
768 return mockHostIface.send(0);
769 };
770
771 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
772 .WillRepeatedly(Invoke(send));
773
774 dataIface.changeHostState(true);
775
776 // Add a PEL and dispatch and send it
777 auto pel = makePEL();
778 auto id = pel->id();
779 repo.add(pel);
780
781 runEvents(sdEvent, 2);
782 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
783 EXPECT_EQ(notifier.queueSize(), 0);
784
785 // The host rejected it.
786 notifier.setBadPEL(id);
787
788 // Doesn't go back on the queue
789 EXPECT_EQ(notifier.queueSize(), 0);
790
791 // Check the state was saved in the PEL itself
792 Repository::LogID i{Repository::LogID::Pel{id}};
793 auto data = repo.getPELData(i);
794 PEL pelFromRepo{*data};
795 EXPECT_EQ(pelFromRepo.hostTransmissionState(),
796 TransmissionState::badPEL);
797
798 dataIface.changeHostState(false);
799
800 // Ensure it doesn't go back on the queue on a power cycle
801 EXPECT_EQ(notifier.queueSize(), 0);
802 }
803
804 // Now restore the repo, and make sure it doesn't come back
805 {
806 Repository repo{repoPath};
807
808 std::unique_ptr<HostInterface> hostIface =
809 std::make_unique<MockHostInterface>(event, dataIface);
810
811 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
812
813 EXPECT_EQ(notifier.queueSize(), 0);
814 }
815}