nvmed: Add support for nvmed data posted to shared_mem
Added a feature option "nvme_shmem" to nvmed, enabling publication
of NVMe sensor values to shared memory segment. This is a requirement
for the PID tlBMC migration, allowing tlBMC to consume sensor data with
lower latency compared to DBus
Find more details here: go/pid-tlbmc-migration
Tested: https://paste.googleplex.com/6120153892913152
Google-Bug-Id: 434974253
Change-Id: I045e955c6d250e51f083daced0433c791b1d3ac7
Signed-off-by: Agrim Bharat <agrimbharat@google.com>
diff --git a/meson.build b/meson.build
index 5f17f7c..95f1522 100644
--- a/meson.build
+++ b/meson.build
@@ -93,6 +93,16 @@
uring,
]
+shared_mem_dep = declare_dependency()
+absl_status_dep = declare_dependency()
+if get_option('nvme_shmem').enabled()
+ add_project_arguments('-DNVMED_ENABLE_SHMEM', language: 'cpp')
+ add_project_arguments('-fconstexpr-ops-limit=134217728', language: 'cpp')
+
+ shared_mem_dep = dependency('shared_memory', required: true)
+ absl_status_dep = dependency('absl_status', required: true)
+endif
+
generated_sources = []
generated_others = []
yaml_sources = []
diff --git a/meson_options.txt b/meson_options.txt
index 4f2e000..cab8924 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -14,4 +14,5 @@
option('insecure-sensor-override', type : 'feature', value : 'disabled', description : 'Enables Sensor override feature without any check.',)
option('build-type', type: 'combo', choices: ['ci', 'bitbake'], value: 'ci', yield: false)
option('single-worker', type : 'feature', value: 'disabled', description : 'Enable single worker per i2c bus')
-option('metric-store', type : 'combo', choices : ['disabled', 'static', 'dynamic'], value : 'disabled', description : 'Metric store options. If disabled, then no metrics will be loaded; if static, then will load all metrics but will not dynamically decide which to load; if dynamic then metric store will load specific metrics dynamically based on conditions.')
\ No newline at end of file
+option('metric-store', type : 'combo', choices : ['disabled', 'static', 'dynamic'], value : 'disabled', description : 'Metric store options. If disabled, then no metrics will be loaded; if static, then will load all metrics but will not dynamically decide which to load; if dynamic then metric store will load specific metrics dynamically based on conditions.')
+option('nvme_shmem', type: 'feature', value: 'disabled', description: 'Enable shared memory for NVMe sensors (PID tlBMC migration).', )
\ No newline at end of file
diff --git a/src/NVMeUtil.hpp b/src/NVMeUtil.hpp
index 0f80f65..694c065 100644
--- a/src/NVMeUtil.hpp
+++ b/src/NVMeUtil.hpp
@@ -3,10 +3,51 @@
#include <boost/asio.hpp>
#include <phosphor-logging/lg2.hpp>
+#include <chrono>
#include <filesystem>
+#include <mutex>
#include <optional>
#include <system_error>
+/**
+ * @brief Logs a message via lg2 at most once every N seconds specific to the
+ * call site.
+ *
+ * This macro utilizes static local variables to maintain state specific
+ * to the exact line of code where the macro is expanded.
+ *
+ * It uses double-checked locking to ensure thread safety if multiple threads
+ * hit the exact same log statement simultaneously.
+ *
+ * @param level The lg2 severity level (e.g., info, error, warning, debug).
+ * @param N The interval in seconds.
+ * @param msg The message format string.
+ * @param ... Optional arguments for the format string.
+ */
+#define LG2_LOG_EVERY_N_SEC(level, N, msg, ...) \
+ do \
+ { \
+ /* The magic happens here: These static variables are unique per */ \
+ /* every distinct place this macro is used in the source code. */ \
+ static std::chrono::steady_clock::time_point _lg2_site_last_time; \
+ static std::mutex _lg2_site_mutex; \
+ \
+ /* Use steady_clock so system time changes don't break the timer */ \
+ auto _lg2_now = std::chrono::steady_clock::now(); \
+ \
+ /* Optimization: Quick check without locking first */ \
+ if (_lg2_now - _lg2_site_last_time >= std::chrono::seconds(N)) \
+ { \
+ std::lock_guard<std::mutex> _lg2_lock(_lg2_site_mutex); \
+ /* Thread Safety: Double-check inside lock */ \
+ if (_lg2_now - _lg2_site_last_time >= std::chrono::seconds(N)) \
+ { \
+ lg2::level(msg, ##__VA_ARGS__); \
+ _lg2_site_last_time = _lg2_now; \
+ } \
+ } \
+ } while (0)
+
namespace nvme
{
static constexpr const char* sensorType = "NVME1000";
@@ -192,7 +233,7 @@
{
return;
}
- timer->expires_from_now(
+ timer->expires_after(
std::chrono::duration_cast<std::chrono::milliseconds>(delay));
timer->async_wait(std::bind_front(detail::pollCtemp<T>, timer, delay,
dataFetcher, dataProcessor));
diff --git a/src/meson.build b/src/meson.build
index 291ac62..ecbb881 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -237,6 +237,13 @@
nvme_dbus_dep
]
+ if get_option('nvme_shmem').enabled()
+ nvme_deps += [
+ absl_status_dep,
+ shared_mem_dep
+ ]
+ endif
+
cxx = meson.get_compiler('cpp')
executable(
diff --git a/src/sensor.hpp b/src/sensor.hpp
index 07926a0..504502b 100644
--- a/src/sensor.hpp
+++ b/src/sensor.hpp
@@ -2,10 +2,12 @@
#include "dbus-sensor_config.h"
+#include "NVMeUtil.hpp"
#include "SensorPaths.hpp"
#include "Thresholds.hpp"
#include "Utils.hpp"
+#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/exception.hpp>
@@ -14,6 +16,12 @@
#include <string>
#include <vector>
+#ifdef NVMED_ENABLE_SHMEM
+#include <absl/status/status.h>
+#include <absl/strings/str_cat.h>
+#include <shared_memory/static_client_impl.h>
+#endif
+
constexpr size_t sensorFailedPollTimeMs = 5000;
// Enable useful logging with sensor instrumentation
@@ -585,5 +593,32 @@
internalSet = true;
updateProperty(sensorInterface, value, newValue, "Value");
internalSet = false;
+
+#ifdef NVMED_ENABLE_SHMEM
+ milotic_tlbmc::IpcClient* shmClientPtr =
+ &milotic_tlbmc::StaticSharedMemoryClient::GetInstance();
+ if (shmClientPtr != nullptr)
+ {
+ std::string sharedMemSensorName = absl::StrCat("sharedmem_",
+ this->name);
+
+ absl::Status status =
+ shmClientPtr->UpdateSensorValue(sharedMemSensorName, newValue);
+
+ if (!status.ok())
+ {
+ LG2_LOG_EVERY_N_SEC(
+ error, 20,
+ "Failed to publish sensor value to shared memory for {SENSOR_NAME}: {STATUS}",
+ "SENSOR_NAME", sharedMemSensorName, "STATUS",
+ status.ToString());
+ }
+ }
+ else
+ {
+ LG2_LOG_EVERY_N_SEC(
+ error, 20, "shmClientPtr is null, cannot update shared memory");
+ }
+#endif
}
};