Implementation of minimum watchdog interval.

Tested: phosphor-watchdog CI unittest - set interval to value smaller than
the minimum interval and test that the minimum interval was set as the
interval.
Change-Id: I88d7ca865ce57eaccea8aaf50396dbb50bd396fb
Signed-off-by: Ofer Yehielli <ofery@google.com>
diff --git a/mainapp.cpp b/mainapp.cpp
index 35ef255..abd4777 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -130,6 +130,10 @@
                  "Should we reset the time remaining any time a postcode "
                  "is signaled.");
 
+    uint64_t minInterval = phosphor::watchdog::DEFAULT_MIN_INTERVAL_MS;
+    app.add_flag("-m,--min_interval", minInterval,
+                 "Set minimum interval for watchdog");
+
     CLI11_PARSE(app, argc, argv);
 
     // Put together a list of actions and associated systemd targets
@@ -218,7 +222,7 @@
 
         // Create a watchdog object
         Watchdog watchdog(bus, path.c_str(), event, std::move(actionTargetMap),
-                          std::move(maybeFallback));
+                          std::move(maybeFallback), minInterval);
 
         std::optional<sdbusplus::bus::match::match> watchPostcodeMatch;
         if (watchPostcodes)
diff --git a/test/watchdog.cpp b/test/watchdog.cpp
index 55502c2..b557e62 100644
--- a/test/watchdog.cpp
+++ b/test/watchdog.cpp
@@ -4,7 +4,10 @@
 #include <thread>
 #include <utility>
 
-using namespace phosphor::watchdog;
+namespace phosphor
+{
+namespace watchdog
+{
 
 WdogTest::Quantum WdogTest::waitForWatchdog(Quantum timeLimit)
 {
@@ -354,8 +357,13 @@
     fallback.interval = static_cast<uint64_t>(fallbackIntervalMs);
     fallback.always = true;
     wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event,
-                                      Watchdog::ActionTargetMap(),
-                                      std::move(fallback));
+                                      Watchdog::ActionTargetMap(), fallback,
+                                      milliseconds(TEST_MIN_INTERVAL).count());
+
+    // Make sure defualt interval is biggger than min interval
+    EXPECT_LT(milliseconds((TEST_MIN_INTERVAL).count()),
+              milliseconds(wdog->interval()));
+
     EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs)));
     EXPECT_FALSE(wdog->enabled());
     auto remaining = milliseconds(wdog->timeRemaining());
@@ -390,3 +398,46 @@
     EXPECT_FALSE(wdog->timerExpired());
     EXPECT_TRUE(wdog->timerEnabled());
 }
+
+/** @brief Test minimal interval
+ *  The minimal interval was set 2 seconds
+ *  Test that when setting interval to 1s , it is still returning 2s
+ */
+TEST_F(WdogTest, verifyMinIntervalSetting)
+{
+    auto newInterval = Quantum(1);
+    auto newIntervalMs = milliseconds(newInterval).count();
+    auto minIntervalMs = milliseconds(TEST_MIN_INTERVAL).count();
+
+    // Check first that the current interval is greater than minInterval
+    EXPECT_LT(minIntervalMs, wdog->interval());
+    // Check that the interval was not set to smaller value than minInterval
+    EXPECT_EQ(minIntervalMs, wdog->interval(newIntervalMs));
+    // Check that the interval was not set to smaller value than minInterval
+    EXPECT_EQ(minIntervalMs, wdog->interval());
+}
+
+/** @brief Test minimal interval
+ *  Initiate default Watchdog in order to get the default
+ *  interval.
+ *  Initiate watchdog with minInterval greater than default
+ *  interval, and make sure the default interval was set to the
+ *  minInterval.
+ */
+TEST_F(WdogTest, verifyConstructorMinIntervalSetting)
+{
+    // Initiate default Watchdog and get the default interval value.
+    wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event);
+    auto defaultInterval = wdog->interval();
+    auto minInterval = defaultInterval + 100;
+    // We initiate a new Watchdog with min interval greater than the default
+    // intrval
+    wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event,
+                                      Watchdog::ActionTargetMap(), std::nullopt,
+                                      minInterval);
+    // Check that the interval was set to the minInterval
+    EXPECT_EQ(minInterval, wdog->interval());
+}
+
+} // namespace watchdog
+} // namespace phosphor
diff --git a/test/watchdog.hpp b/test/watchdog.hpp
index df628c3..87166f4 100644
--- a/test/watchdog.hpp
+++ b/test/watchdog.hpp
@@ -7,9 +7,16 @@
 
 #include <gtest/gtest.h>
 
+namespace phosphor
+{
+namespace watchdog
+{
+
 using namespace std::chrono;
 using namespace std::chrono_literals;
 
+constexpr auto TEST_MIN_INTERVAL = duration<uint64_t, std::deci>(2);
+
 // Test Watchdog functionality
 class WdogTest : public ::testing::Test
 {
@@ -22,9 +29,12 @@
     WdogTest() :
         event(sdeventplus::Event::get_default()),
         bus(sdbusplus::bus::new_default()),
-        wdog(std::make_unique<phosphor::watchdog::Watchdog>(bus, TEST_PATH,
-                                                            event)),
+        wdog(std::make_unique<Watchdog>(
+            bus, TEST_PATH, event, Watchdog::ActionTargetMap(), std::nullopt,
+            milliseconds(TEST_MIN_INTERVAL).count())),
+
         defaultInterval(Quantum(3))
+
     {
         wdog->interval(milliseconds(defaultInterval).count());
         // Initially the watchdog would be disabled
@@ -38,7 +48,7 @@
     sdbusplus::bus::bus bus;
 
     // Watchdog object
-    std::unique_ptr<phosphor::watchdog::Watchdog> wdog;
+    std::unique_ptr<Watchdog> wdog;
 
     // This is the default interval as given in Interface definition
     Quantum defaultInterval;
@@ -53,3 +63,6 @@
     // disabled or have its timeRemaining reset.
     Quantum waitForWatchdog(Quantum timeLimit);
 };
+
+} // namespace watchdog
+} // namespace phosphor
diff --git a/watchdog.cpp b/watchdog.cpp
index bc3ba95..9090760 100644
--- a/watchdog.cpp
+++ b/watchdog.cpp
@@ -1,5 +1,6 @@
 #include "watchdog.hpp"
 
+#include <algorithm>
 #include <chrono>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/log.hpp>
@@ -92,6 +93,12 @@
     return WatchdogInherits::timeRemaining(value);
 }
 
+// Set value of Interval
+uint64_t Watchdog::interval(uint64_t value)
+{
+    return WatchdogInherits::interval(std::max(value, minInterval));
+}
+
 // Optional callback function on timer expiration
 void Watchdog::timeOutHandler()
 {
diff --git a/watchdog.hpp b/watchdog.hpp
index 34b0411..7de9bb3 100644
--- a/watchdog.hpp
+++ b/watchdog.hpp
@@ -15,6 +15,7 @@
 namespace watchdog
 {
 
+constexpr auto DEFAULT_MIN_INTERVAL_MS = 0;
 namespace Base = sdbusplus::xyz::openbmc_project::State::server;
 using WatchdogInherits = sdbusplus::server::object::object<Base::Watchdog>;
 
@@ -62,12 +63,15 @@
     Watchdog(sdbusplus::bus::bus& bus, const char* objPath,
              const sdeventplus::Event& event,
              ActionTargetMap&& actionTargetMap = {},
-             std::optional<Fallback>&& fallback = std::nullopt) :
+             std::optional<Fallback>&& fallback = std::nullopt,
+             uint64_t minInterval = DEFAULT_MIN_INTERVAL_MS) :
         WatchdogInherits(bus, objPath),
         bus(bus), actionTargetMap(std::move(actionTargetMap)),
-        fallback(std::move(fallback)),
+        fallback(std::move(fallback)), minInterval(minInterval),
         timer(event, std::bind(&Watchdog::timeOutHandler, this))
     {
+        // We set the watchdog interval with the default value.
+        interval(interval());
         // We need to poke the enable mechanism to make sure that the timer
         // enters the fallback state if the fallback is always enabled.
         tryFallbackOrDisable();
@@ -116,6 +120,23 @@
      */
     uint64_t timeRemaining(uint64_t value) override;
 
+    /** @brief Get value of Interval
+     *
+     *
+     *  @return: current interval
+     *
+     */
+    using WatchdogInherits::interval;
+
+    /** @brief Set value of Interval
+     *
+     *  @param[in] value - interval time to set
+     *
+     *  @return: interval that was set
+     *
+     */
+    uint64_t interval(uint64_t value);
+
     /** @brief Tells if the referenced timer is expired or not */
     inline auto timerExpired() const
     {
@@ -138,6 +159,9 @@
     /** @brief Fallback timer options */
     std::optional<Fallback> fallback;
 
+    /** @brief Minimum watchdog interval value */
+    uint64_t minInterval;
+
     /** @brief Contained timer object */
     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;