Skip to content

Commit

Permalink
Sensor service respin foundation implementation
Browse files Browse the repository at this point in the history
Summary:
## General Idea
Unlike PM respins where device creation order is sometimes required, sensors can be read in any orders
(cc mikechoifb if wrong).

We can afford to separate out the sensors for each versions to prevent config duplications as much as possible.
Versions are separated by the following fields:
* ProductProductionState
* ProductVersion
* ProductSubVersion

Sensors defined in a version means they only exist in that version. This means sensors defined in PmSensor are common for all platform versions.
If Sensor service discovers a set of sensors exist the particular version, it will reads their values along with common sensors.
Sensor Service will know which set of sensors to read by comparing PmUnit's version against a list VersionedPmSensors.
It will pick the largest VersionPmSensors which PmUnitVersions is greater than.

## PmUnitInfoFetcher
As name suggests, fetches PmUnitInfo. How it will do, still undecided. So split out class for testing and modularization.
For now, it will return null so Sensor service ignores versioned sensors.

Reviewed By: mikechoifb

Differential Revision: D61564795

fbshipit-source-id: d49d43a23f53389c31d21e222a9f12de85460797
  • Loading branch information
Justin Kim authored and facebook-github-bot committed Aug 23, 2024
1 parent 9f68006 commit 1b0a6fe
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 5 deletions.
1 change: 1 addition & 0 deletions cmake/PlatformSensorService.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ add_library(sensor_service_lib
fboss/platform/sensor_service/SensorServiceImpl.cpp
fboss/platform/sensor_service/SensorServiceThriftHandler.cpp
fboss/platform/sensor_service/oss/FsdbSyncer.cpp
fboss/platform/sensor_service/PmUnitInfoFetcher.cpp
)

target_link_libraries(sensor_service_lib
Expand Down
10 changes: 10 additions & 0 deletions fboss/platform/sensor_service/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cpp_library(
"SensorServiceImpl.h",
],
exported_deps = [
":pmunit_info_fetcher",
":sensor_service_stats-cpp2-types",
":utils",
"//fb303:service_data",
Expand Down Expand Up @@ -60,12 +61,21 @@ cpp_library(
],
exported_deps = [
"fbsource//third-party/exprtk:exprtk",
":pmunit_info_fetcher",
"//fboss/platform/sensor_service/if:sensor_config-cpp2-types",
],
exported_external_deps = [
"re2",
],
)

cpp_library(
name = "pmunit_info_fetcher",
srcs = [
"PmUnitInfoFetcher.cpp",
],
)

cpp_binary(
name = "sensor_service",
srcs = [
Expand Down
11 changes: 11 additions & 0 deletions fboss/platform/sensor_service/PmUnitInfoFetcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.

#include "fboss/platform/sensor_service/PmUnitInfoFetcher.h"

namespace facebook::fboss::platform::sensor_service {
std::optional<std::array<int16_t, 3>> PmUnitInfoFetcher::fetch(
const std::string& /* slotPath */,
const std::string& /* pmUnitName */) const {
return std::nullopt;
}
} // namespace facebook::fboss::platform::sensor_service
21 changes: 21 additions & 0 deletions fboss/platform/sensor_service/PmUnitInfoFetcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.

#pragma once

#include <array>
#include <optional>
#include <string>

namespace facebook::fboss::platform::sensor_service {
class PmUnitInfoFetcher {
public:
virtual ~PmUnitInfoFetcher() = default;
// Returns {ProductProductionState,ProductVersion,ProductSubVersion}
// TODO: Need to figure out how we're going to fetch (thrift? file? manual
// eeprom reading?)
virtual std::optional<std::array<int16_t, 3>> fetch(
const std::string& slotPath,
const std::string& pmUnitName) const;
};

} // namespace facebook::fboss::platform::sensor_service
21 changes: 20 additions & 1 deletion fboss/platform/sensor_service/SensorServiceImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,26 @@ void SensorServiceImpl::fetchSensorData() {
if (!sensorConfig_.pmUnitSensorsList()->empty()) {
XLOG(INFO) << "Fetching using PM based sensor structs...";
for (const auto& pmUnitSensors : *sensorConfig_.pmUnitSensorsList()) {
for (const auto& sensor : *pmUnitSensors.sensors()) {
auto pmSensors = *pmUnitSensors.sensors();
if (auto versionedPmSensors = Utils().resolveVersionedSensors(
pmUnitInfoFetcher_,
*pmUnitSensors.slotPath(),
*pmUnitSensors.pmUnitName(),
*pmUnitSensors.versionedSensors())) {
XLOG(INFO) << fmt::format(
"Resolved to versionedPmSensors of productProductionState({}) "
"prductVersion({}) productSubVersion({}) for pmUnit {} at {}",
*versionedPmSensors->productProductionState(),
*versionedPmSensors->productVersion(),
*versionedPmSensors->productSubVersion(),
*pmUnitSensors.slotPath(),
*pmUnitSensors.pmUnitName());
pmSensors.insert(
pmSensors.end(),
versionedPmSensors->sensors()->begin(),
versionedPmSensors->sensors()->end());
}
for (const auto& sensor : pmSensors) {
fetchSensorDataImpl(sensor, readFailures, polledData);
}
}
Expand Down
2 changes: 2 additions & 0 deletions fboss/platform/sensor_service/SensorServiceImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <folly/Synchronized.h>

#include "fboss/platform/sensor_service/FsdbSyncer.h"
#include "fboss/platform/sensor_service/PmUnitInfoFetcher.h"
#include "fboss/platform/sensor_service/if/gen-cpp2/sensor_config_types.h"
#include "fboss/platform/sensor_service/if/gen-cpp2/sensor_service_types.h"

Expand Down Expand Up @@ -69,6 +70,7 @@ class SensorServiceImpl {
std::optional<std::chrono::time_point<std::chrono::steady_clock>>
publishedStatsToFsdbAt_;
SensorConfig sensorConfig_{};
PmUnitInfoFetcher pmUnitInfoFetcher_{};
};

} // namespace facebook::fboss::platform::sensor_service
47 changes: 45 additions & 2 deletions fboss/platform/sensor_service/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@

#include "fboss/platform/sensor_service/Utils.h"

#include <re2/re2.h>
#include <array>
#include <chrono>
#include <unordered_set>
#include <iostream>

#include <exprtk.hpp>
#include <re2/re2.h>

namespace {
// Compare the two array reprsentation of
// productionState,productVersion,productSubVersion.
// Return true if l1 >= l2, false otherwise.
bool greaterEqual(
std::array<int16_t, 3> l1,
std::array<int16_t, 3> l2,
uint8_t i = 0) {
if (i == 3) {
return true;
}
if (l1[i] > l2[i]) {
return true;
}
if (l1[i] < l2[i]) {
return false;
}
return greaterEqual(l1, l2, ++i);
}
} // namespace
namespace facebook::fboss::platform::sensor_service {

uint64_t Utils::nowInSecs() {
Expand Down Expand Up @@ -40,4 +61,26 @@ float Utils::computeExpression(
return expr.value();
}

std::optional<VersionedPmSensor> Utils::resolveVersionedSensors(
const PmUnitInfoFetcher& fetcher,
const std::string& slotPath,
const std::string& pmUnitName,
const std::vector<VersionedPmSensor>& versionedSensors) {
const auto pmUnitInfo = fetcher.fetch(slotPath, pmUnitName);
if (!pmUnitInfo) {
return std::nullopt;
}
std::optional<VersionedPmSensor> resolvedVersionedSensor{std::nullopt};
for (const auto& versionedSensor : versionedSensors) {
if (greaterEqual(
*pmUnitInfo,
{*versionedSensor.productProductionState(),
*versionedSensor.productVersion(),
*versionedSensor.productSubVersion()})) {
resolvedVersionedSensor = versionedSensor;
}
}
return resolvedVersionedSensor;
}

} // namespace facebook::fboss::platform::sensor_service
11 changes: 11 additions & 0 deletions fboss/platform/sensor_service/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
#pragma once

#include <string>
#include <vector>

#include "fboss/platform/sensor_service/PmUnitInfoFetcher.h"
#include "fboss/platform/sensor_service/if/gen-cpp2/sensor_config_types.h"

namespace facebook::fboss::platform::sensor_service {
using namespace facebook::fboss::platform::sensor_config;

class Utils {
public:
Expand All @@ -21,5 +26,11 @@ class Utils {
const std::string& expression,
float input,
const std::string& symbol = "x");

std::optional<VersionedPmSensor> resolveVersionedSensors(
const PmUnitInfoFetcher& fetcher,
const std::string& slotPath,
const std::string& pmUnitName,
const std::vector<VersionedPmSensor>& versionedSensors);
};
} // namespace facebook::fboss::platform::sensor_service
24 changes: 23 additions & 1 deletion fboss/platform/sensor_service/if/sensor_config.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,39 @@ struct PmSensor {
5: SensorType type;
}

// `VersionedPmSensor`: Describes a set of sensors which would exist in Platforms with
// minimum productProductionState, productVersion and productSubVersion.
//
// `sensors`: A set of sensors belong to this version. They're mutually exclusive from other versions.
// If there're any carry-over sensors in the other versions, they must be redefined in that version.
//
// `productProductionState`: Minimum productProductionState (EEPROM V5 Type 8).
//
// `productVersion`: Minimum productVersion (EEPROM V5 Type 9).
//
// `productSubVersion`: Minimum productSubVersion (EEPROM V5 Type 10).
struct VersionedPmSensor {
1: list<PmSensor> sensors;
2: i16 productProductionState;
3: i16 productVersion;
4: i16 productSubVersion;
}

// `PmUnitSensors`: Describes every sensor in PmUnit.
//
// `slotPath`: Refers to the location of slot in the platform.
//
// `pmUnitName`: Name of the PmUnit.
//
// `sensors`: List of pmSensor. See above PmSensor definition.
// `sensors`: List of common pmSensor across respins. See above PmSensor definition.
//
// `versionedSensors`: List of versionedPmSensors for specific respin.
// See above VersionedPmSensor definition.
struct PmUnitSensors {
1: string slotPath;
2: string pmUnitName;
3: list<PmSensor> sensors;
4: list<VersionedPmSensor> versionedSensors;
}

typedef string SensorName
Expand Down
1 change: 1 addition & 0 deletions fboss/platform/sensor_service/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ cpp_unittest(
"UtilsTest.cpp",
],
deps = [
"fbsource//third-party/googletest:gmock",
"//fboss/platform/sensor_service:utils",
],
)
70 changes: 69 additions & 1 deletion fboss/platform/sensor_service/test/UtilsTest.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "fboss/platform/sensor_service/Utils.h"

using namespace ::testing;
namespace facebook::fboss::platform::sensor_service {
TEST(computeExpressionTests, Equal) {
class MockPmUnitInfoFetcher : public PmUnitInfoFetcher {
public:
explicit MockPmUnitInfoFetcher() : PmUnitInfoFetcher() {}
MOCK_METHOD(
(std::optional<std::array<int16_t, 3>>),
fetch,
(const std::string&, const std::string&),
(const));
};

class UtilsTests : public testing::Test {
public:
VersionedPmSensor createVersionedPmSensor(
uint productProductionState,
uint productVersion,
uint productSubVersion) {
VersionedPmSensor versionedPmSensor;
versionedPmSensor.productProductionState() = productProductionState;
versionedPmSensor.productVersion() = productVersion;
versionedPmSensor.productSubVersion() = productSubVersion;
return versionedPmSensor;
}
bool isEqual(VersionedPmSensor s1, VersionedPmSensor s2) {
return s1.productProductionState() == s2.productProductionState() &&
s1.productVersion() == s2.productVersion() &&
s1.productSubVersion() == s2.productSubVersion();
}
MockPmUnitInfoFetcher fetcher_;
std::string slotPath_;
std::string pmUnitName_;
};

TEST_F(UtilsTests, Equal) {
EXPECT_FLOAT_EQ(Utils::computeExpression("x * 0.1", 10.0), 1.0);
EXPECT_FLOAT_EQ(Utils::computeExpression("x * 0.1/100", 10.0), 0.01);
EXPECT_FLOAT_EQ(
Expand All @@ -21,4 +55,38 @@ TEST(computeExpressionTests, Equal) {
"(@ / 0.1+300)/ (1000*10 + @ * 10000)", 30.0, "x"),
0.0019354839);
}

TEST_F(UtilsTests, PmUnitInfoFetcherTest) {
std::optional<VersionedPmSensor> resolvedVersionedSensor;
// Case-1: Non existence PmUnitInfo (e.g no IDPROM)
EXPECT_CALL(fetcher_, fetch(_, _)).WillOnce(Return(std::nullopt));
EXPECT_EQ(
Utils().resolveVersionedSensors(fetcher_, slotPath_, pmUnitName_, {}),
std::nullopt);
// Case-2: Non-matching VersionedPmSensor
EXPECT_CALL(fetcher_, fetch(_, _))
.WillOnce(Return(std::array<int16_t, 3>{1, 0, 20}));
resolvedVersionedSensor = Utils().resolveVersionedSensors(
fetcher_, slotPath_, pmUnitName_, {createVersionedPmSensor(1, 1, 2)});
EXPECT_EQ(resolvedVersionedSensor, std::nullopt);
// Case-3a: Matching Single VersionedPmSensors
EXPECT_CALL(fetcher_, fetch(_, _))
.WillOnce(Return(std::array<int16_t, 3>{1, 1, 20}));
resolvedVersionedSensor = Utils().resolveVersionedSensors(
fetcher_, slotPath_, pmUnitName_, {createVersionedPmSensor(1, 1, 2)});
EXPECT_NE(resolvedVersionedSensor, std::nullopt);
EXPECT_TRUE(
isEqual(*resolvedVersionedSensor, createVersionedPmSensor(1, 1, 2)));
// Case-3b: Matching Multiple VersionedPmSensors
EXPECT_CALL(fetcher_, fetch(_, _))
.WillOnce(Return(std::array<int16_t, 3>{1, 1, 20}));
resolvedVersionedSensor = Utils().resolveVersionedSensors(
fetcher_,
slotPath_,
pmUnitName_,
{createVersionedPmSensor(1, 1, 2), createVersionedPmSensor(1, 1, 4)});
EXPECT_NE(resolvedVersionedSensor, std::nullopt);
EXPECT_TRUE(
isEqual(*resolvedVersionedSensor, createVersionedPmSensor(1, 1, 4)));
}
} // namespace facebook::fboss::platform::sensor_service

0 comments on commit 1b0a6fe

Please sign in to comment.