summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLei YU <mine260309@gmail.com>2017-03-07 17:49:17 +0800
committerLei YU <mine260309@gmail.com>2017-08-01 10:31:07 +0800
commitaf5abc5785535f4e059a35be60dbbd012c61facf (patch)
tree47c8484a1e366cc66fa14c1723e88213273e43fc
parente7abcdc7bb9cc3b5e36d829aa777b2c544792d10 (diff)
downloadphosphor-time-manager-af5abc5785535f4e059a35be60dbbd012c61facf.tar.gz
phosphor-time-manager-af5abc5785535f4e059a35be60dbbd012c61facf.zip
Initial implementation of HostEpoch
When host time is set, the diff between the BmcTime and the value is saved to persistent storage; When host time is retrieved, return the BmcTime plus the diff as host's time. Add the unit test cases for HostEpoch. Change-Id: Ia55b93bfcba4f226ceaed8491136ea7afda7bd77 Signed-off-by: Lei YU <mine260309@gmail.com>
-rw-r--r--Makefile.am3
-rw-r--r--bmc_epoch.cpp4
-rw-r--r--bmc_epoch.hpp4
-rw-r--r--configure.ac4
-rw-r--r--host_epoch.cpp74
-rw-r--r--host_epoch.hpp69
-rw-r--r--main.cpp2
-rw-r--r--test/Makefile.am3
-rw-r--r--test/TestHostEpoch.cpp173
9 files changed, 330 insertions, 6 deletions
diff --git a/Makefile.am b/Makefile.am
index 3eef41a..0af260f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,8 @@ noinst_LTLIBRARIES = libtimemanager.la
libtimemanager_la_SOURCES = \
epoch_base.cpp \
- bmc_epoch.cpp
+ bmc_epoch.cpp \
+ host_epoch.cpp
phosphor_timemanager_SOURCES = \
main.cpp
diff --git a/bmc_epoch.cpp b/bmc_epoch.cpp
index 5923e8e..ed482ad 100644
--- a/bmc_epoch.cpp
+++ b/bmc_epoch.cpp
@@ -44,6 +44,6 @@ uint64_t BmcEpoch::elapsed(uint64_t value)
return value;
}
-}
-}
+} // namespace time
+} // namespace phosphor
diff --git a/bmc_epoch.hpp b/bmc_epoch.hpp
index 56d78e9..8c841a7 100644
--- a/bmc_epoch.hpp
+++ b/bmc_epoch.hpp
@@ -35,5 +35,5 @@ class BmcEpoch : public EpochBase
uint64_t elapsed(uint64_t value) override;
};
-}
-}
+} // namespace time
+} // namespace phosphor
diff --git a/configure.ac b/configure.ac
index 03f8347..7a2ee4e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,6 +64,10 @@ AC_ARG_VAR(OBJPATH_HOST, [The host epoch Dbus root])
AS_IF([test "x$OBJPATH_HOST" == "x"], [OBJPATH_HOST="/xyz/openbmc_project/time/host"])
AC_DEFINE_UNQUOTED([OBJPATH_HOST], ["$OBJPATH_HOST"], [The host epoch Dbus root])
+AC_ARG_VAR(HOST_OFFSET_FILE, [The file to save host time offset])
+AS_IF([test "x$HOST_OFFSET_FILE" == "x"], [HOST_OFFSET_FILE="/var/lib/obmc/saved_host_offset"])
+AC_DEFINE_UNQUOTED([HOST_OFFSET_FILE], ["$HOST_OFFSET_FILE"], [The file to save host time offset])
+
AC_CONFIG_FILES([Makefile test/Makefile])
AC_OUTPUT
diff --git a/host_epoch.cpp b/host_epoch.cpp
new file mode 100644
index 0000000..f45581d
--- /dev/null
+++ b/host_epoch.cpp
@@ -0,0 +1,74 @@
+#include "host_epoch.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+#include <fstream>
+
+namespace phosphor
+{
+namespace time
+{
+using namespace sdbusplus::xyz::openbmc_project::Time;
+using namespace phosphor::logging;
+
+HostEpoch::HostEpoch(sdbusplus::bus::bus& bus,
+ const char* objPath)
+ : EpochBase(bus, objPath),
+ offset(readData<decltype(offset)::rep>(offsetFile))
+{
+ // Empty
+}
+
+uint64_t HostEpoch::elapsed() const
+{
+ // It does not needs to check owner when getting time
+ return (getTime() + offset).count();
+}
+
+uint64_t HostEpoch::elapsed(uint64_t value)
+{
+ if (timeOwner == Owner::BMC)
+ {
+ log<level::ERR>("Setting HostTime in BMC owner is not allowed");
+ // TODO: throw NotAllowed exception
+ return 0;
+ }
+
+ // TODO: implement the logic of setting host time
+ // based on timeOwner and timeMode
+
+ auto time = std::chrono::microseconds(value);
+ offset = time - getTime();
+
+ // Store the offset to file
+ writeData(offsetFile, offset.count());
+
+ server::EpochTime::elapsed(value);
+ return value;
+}
+
+template <typename T>
+T HostEpoch::readData(const char* fileName)
+{
+ T data{};
+ std::ifstream fs(fileName);
+ if (fs.is_open())
+ {
+ fs >> data;
+ }
+ return data;
+}
+
+template <typename T>
+void HostEpoch::writeData(const char* fileName, T&& data)
+{
+ std::ofstream fs(fileName, std::ios::out);
+ if (fs.is_open())
+ {
+ fs << std::forward<T>(data);
+ }
+}
+
+} // namespace time
+} // namespace phosphor
+
diff --git a/host_epoch.hpp b/host_epoch.hpp
new file mode 100644
index 0000000..1252ff9
--- /dev/null
+++ b/host_epoch.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "config.h"
+#include "epoch_base.hpp"
+
+#include <chrono>
+
+namespace phosphor
+{
+namespace time
+{
+
+/** @class HostEpoch
+ * @brief OpenBMC HOST EpochTime implementation.
+ * @details A concrete implementation for xyz.openbmc_project.Time.EpochTime
+ * DBus API for HOST's epoch time.
+ */
+class HostEpoch : public EpochBase
+{
+ public:
+ friend class TestHostEpoch;
+ HostEpoch(sdbusplus::bus::bus& bus,
+ const char* objPath);
+
+ /**
+ * @brief Get value of Elapsed property
+ *
+ * @return The elapsed microseconds since UTC
+ **/
+ uint64_t elapsed() const override;
+
+ /**
+ * @brief Set value of Elapsed property
+ *
+ * @param[in] value - The microseconds since UTC to set
+ *
+ * @return The updated elapsed microseconds since UTC
+ **/
+ uint64_t elapsed(uint64_t value) override;
+
+ private:
+ /** @brief The diff between BMC and Host time */
+ std::chrono::microseconds offset;
+
+ /** @brief The file to store the offset in File System.
+ * Read back when starts
+ **/
+ static constexpr auto offsetFile = HOST_OFFSET_FILE;
+
+ /** @brief Read data with type T from file
+ *
+ * @param[in] fileName - The name of file to read from
+ *
+ * @return The data with type T
+ */
+ template <typename T>
+ static T readData(const char* fileName);
+
+ /** @brief Write data with type T to file
+ *
+ * @param[in] fileName - The name of file to write to
+ * @param[in] data - The data with type T to write to file
+ */
+ template <typename T>
+ static void writeData(const char* fileName, T&& data);
+};
+
+} // namespace time
+} // namespace phosphor
diff --git a/main.cpp b/main.cpp
index 05ac453..b3f4b57 100644
--- a/main.cpp
+++ b/main.cpp
@@ -2,11 +2,13 @@
#include "config.h"
#include "bmc_epoch.hpp"
+#include "host_epoch.hpp"
int main()
{
auto bus = sdbusplus::bus::new_default();
phosphor::time::BmcEpoch bmc(bus, OBJPATH_BMC);
+ phosphor::time::HostEpoch host(bus,OBJPATH_HOST);
bus.request_name(BUSNAME);
diff --git a/test/Makefile.am b/test/Makefile.am
index 26c4c15..bdd258e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,7 +7,8 @@ check_PROGRAMS += test
test_SOURCES = \
TestEpochBase.cpp \
- TestBmcEpoch.cpp
+ TestBmcEpoch.cpp \
+ TestHostEpoch.cpp
test_LDADD = $(top_builddir)/libtimemanager.la
diff --git a/test/TestHostEpoch.cpp b/test/TestHostEpoch.cpp
new file mode 100644
index 0000000..dca8d72
--- /dev/null
+++ b/test/TestHostEpoch.cpp
@@ -0,0 +1,173 @@
+#include <sdbusplus/bus.hpp>
+#include <gtest/gtest.h>
+
+#include "host_epoch.hpp"
+#include "config.h"
+
+namespace phosphor
+{
+namespace time
+{
+
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
+class TestHostEpoch : public testing::Test
+{
+ public:
+ using Mode = EpochBase::Mode;
+ using Owner = EpochBase::Owner;
+
+ sdbusplus::bus::bus bus;
+ HostEpoch hostEpoch;
+
+ static constexpr auto FILE_NOT_EXIST = "path/to/file-not-exist";
+ static constexpr auto FILE_OFFSET = "saved_host_offset";
+ static constexpr auto delta = 2s;
+
+ TestHostEpoch()
+ : bus(sdbusplus::bus::new_default()),
+ hostEpoch(bus, OBJPATH_HOST)
+ {
+ // Make sure the file does not exist
+ std::remove(FILE_NOT_EXIST);
+ }
+ ~TestHostEpoch()
+ {
+ // Cleanup test file
+ std::remove(FILE_OFFSET);
+ }
+
+ // Proxies for HostEpoch's private members and functions
+ Mode getTimeMode()
+ {
+ return hostEpoch.timeMode;
+ }
+ Owner getTimeOwner()
+ {
+ return hostEpoch.timeOwner;
+ }
+ template <typename T>
+ T readData(const char* fileName)
+ {
+ return HostEpoch::readData<T>(fileName);
+ }
+ template <typename T>
+ void writeData(const char* fileName, T&& data)
+ {
+ HostEpoch::writeData<T>(fileName, std::forward<T>(data));
+ }
+ microseconds getOffset()
+ {
+ return hostEpoch.offset;
+ }
+ void setTimeOwner(Owner owner)
+ {
+ hostEpoch.timeOwner = owner;
+ }
+};
+
+TEST_F(TestHostEpoch, empty)
+{
+ EXPECT_EQ(Mode::NTP, getTimeMode());
+ EXPECT_EQ(Owner::BMC, getTimeOwner());
+}
+
+TEST_F(TestHostEpoch, readDataFileNotExist)
+{
+ // When file does not exist, the default offset shall be 0
+ microseconds offset(0);
+ auto value = readData<decltype(offset)::rep>(FILE_NOT_EXIST);
+ EXPECT_EQ(0, value);
+}
+
+TEST_F(TestHostEpoch, writeAndReadData)
+{
+ // Write offset to file
+ microseconds offsetToWrite(1234567);
+ writeData<decltype(offsetToWrite)::rep>(FILE_OFFSET, offsetToWrite.count());
+
+ // Read it back
+ microseconds offsetToRead;
+ offsetToRead = microseconds(
+ readData<decltype(offsetToRead)::rep>(FILE_OFFSET));
+ EXPECT_EQ(offsetToWrite, offsetToRead);
+}
+
+TEST_F(TestHostEpoch, setElapsedNotAllowed)
+{
+ // By default offset shall be 0
+ EXPECT_EQ(0, getOffset().count());
+
+ // Set time in BMC mode is not allowed,
+ // so verify offset is still 0 after set time
+ microseconds diff = 1min;
+ hostEpoch.elapsed(hostEpoch.elapsed() + diff.count());
+ EXPECT_EQ(0, getOffset().count());
+}
+
+TEST_F(TestHostEpoch, setElapsedInFutureAndGet)
+{
+ // Set to HOST owner so that we can set elapsed
+ setTimeOwner(Owner::HOST);
+
+ // Get current time, and set future +1min time
+ auto t1 = hostEpoch.elapsed();
+ EXPECT_NE(0, t1);
+ microseconds diff = 1min;
+ auto t2 = t1 + diff.count();
+ hostEpoch.elapsed(t2);
+
+ // Verify that the offset shall be positive,
+ // and less or equal to diff, and shall be not too less.
+ auto offset = getOffset();
+ EXPECT_GT(offset, microseconds(0));
+ EXPECT_LE(offset, diff);
+ diff -= delta;
+ EXPECT_GE(offset, diff);
+
+ // Now get time shall be around future +1min time
+ auto epochNow = duration_cast<microseconds>(
+ system_clock::now().time_since_epoch()).count();
+ auto elapsedGot = hostEpoch.elapsed();
+ EXPECT_LT(epochNow, elapsedGot);
+ auto epochDiff = elapsedGot - epochNow;
+ diff = 1min;
+ EXPECT_GT(epochDiff, (diff - delta).count());
+ EXPECT_LT(epochDiff, (diff + delta).count());
+}
+
+TEST_F(TestHostEpoch, setElapsedInPastAndGet)
+{
+ // Set to HOST owner so that we can set elapsed
+ setTimeOwner(Owner::HOST);
+
+ // Get current time, and set past -1min time
+ auto t1 = hostEpoch.elapsed();
+ EXPECT_NE(0, t1);
+ microseconds diff = 1min;
+ auto t2 = t1 - diff.count();
+ hostEpoch.elapsed(t2);
+
+ // Verify that the offset shall be negative, and the absolute value
+ // shall be equal or greater than diff, and shall not be too greater
+ auto offset = getOffset();
+ EXPECT_LT(offset, microseconds(0));
+ offset = -offset;
+ EXPECT_GE(offset, diff);
+ diff += 10s;
+ EXPECT_LE(offset, diff);
+
+ // Now get time shall be around past -1min time
+ auto epochNow = duration_cast<microseconds>(
+ system_clock::now().time_since_epoch()).count();
+ auto elapsedGot = hostEpoch.elapsed();
+ EXPECT_LT(elapsedGot, epochNow);
+ auto epochDiff = epochNow - elapsedGot;
+ diff = 1min;
+ EXPECT_GT(epochDiff, (diff - delta).count());
+ EXPECT_LT(epochDiff, (diff + delta).count());
+}
+
+}
+}
OpenPOWER on IntegriCloud