Conversation
… platform API
`thermalctld` polls thermal and fan sensors in the pmon container and writes the
results to STATE_DB. In Python it imports `sonic_platform`, which loads the
vendor plugin at startup and keeps a full interpreter resident for the life of
the container. The Mellanox plugin reads hw-management sysfs -- pure file I/O
with no native-library requirement -- so a Rust implementation does the same
work with a fraction of the memory.
This adds the build glue and the two vendor crates. The daemon itself and the
vendor-neutral traits it compiles against live in the sonic-platform-daemons
and sonic-platform-common submodules.
* platform/mellanox/mlnx-platform-api-rs -- the Mellanox platform API, sitting
next to the Python mlnx-platform-api rather than under src/, because it is
vendor code. Ports device_data.py, fan.py, fan_drawer.py, thermal.py, psu,
pdb, the fan-drawer LEDs, the SPC1 pre-feed step, liquid-cooling and leak
sensors, the SmartSwitch DPU feed and ThermalUpdater.
* platform/mellanox/hw-management-rs -- the writer half of hw-management's
independent-mode interface, a port of
hw_management_independent_mode_update.py and the DPU thermal helpers.
hw-management publishes its file layout to Python only, so this is a copy of
that layout rather than a call into it.
* rules/sonic-thermalctld-rs.{mk,dep} -- the package. Names no vendor and no
platform, and on a platform that does not opt in it is never built and never
enters the pmon image.
A vendor opts in from its own rules.mk, and those two lines are the whole
coupling:
$(SONIC_THERMALCTLD_RS)_BUILD_ENV = VENDOR_PLATFORM_RS_PATH=$(PROJECT_ROOT)/$(PLATFORM_PATH)/mlnx-platform-api-rs
$(DOCKER_PLATFORM_MONITOR)_DEPENDS += $(SONIC_THERMALCTLD_RS)
The vendor crate is compiled from source into the daemon: no prebuilt library
and no ABI boundary. `mlnx-platform-api-rs.mk` adds a SONIC_MAKE_FILES target so
it can be built and unit-tested on its own -- the counterpart of building the
Python platform API's wheel -- and appends it to
$(DOCKER_PLATFORM_MONITOR)_FILES, so an ordinary pmon build runs the vendor
tests too.
Behaviour beyond the base thermal/fan feed, all of it matching the Python
daemon: per-component polling intervals from platform.json; ASIC, module and DPU
feed cadences from tc_config.json's dev_parameters, matched with the same regex
semantics; fan drawer LEDs including the capability file and the _blink suffix;
module vendor data and the module counter, cleared once when a module goes
absent; TEMPERATURE_INFO_{slot} on CHASSIS_STATE_DB -- not STATE_DB -- for a
modular chassis or a SmartSwitch DPU; leak detection on its own interval;
Switch-BMC mirroring on the host and event-log teeing on the BMC; the
SmartSwitch DPU thermal feed, where an offline DPU is cleared once on the
transition and an unparsable field is written as a fault code rather than a
plausible zero; and liquid-cooled platforms running no ASIC/module feed and
never writing suspend, matching thermal_manager.py's three-way branch.
Two details worth calling out. Python clears every ASIC's and module's thermal
data from an atexit handler (thermal_updater.py:64-81, :92) and suspends
hw-management-tc in deinit(); both are reproduced in that order. The release
profile sets panic = "abort", so nothing unwinds and Drop never runs -- a panic
hook does both instead and chains to the previous hook so the message is still
logged. It is installed only where the feed actually starts, so a liquid-cooled
platform is unaffected. This is the one place the port deliberately does more
than Python, whose main loop has no try/finally and so skips the suspend write
on an unhandled exception, leaving tc running against cleared data.
The other is the absent-device rule: Python withholds a device's derived fields
when the device could not be answered. An absent fan writes N/A for speed,
speed_target, is_under_speed, is_over_speed, status and direction; an unreadable
sensor writes N/A for its recorded extremes and all four thresholds. Both row
builders do the same.
pmon_daemon_control.json on the opted-in platforms selects which daemon runs, so
switching back is a key and a pmon restart with no rebuild. The Python
thermalctld stays installed and stays the default.
Signed-off-by: ganglyu <glv@nvidia.com>
Collaborator
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
platform/mellanox/rustfmt.toml and ci-* targets in mlnx-platform-api-rs's Makefile, covering both Rust crates here -- rustfmt finds the config by walking up, so one file serves both. The policy and the target names are sonic-dash-ha's, which is the only other Rust in SONiC, so a contributor moving between the two repositories runs the same commands and reads the same style. `make ci-all` is `cargo fmt --check`, clippy with clippy::all denied, build and doc with warnings denied, and the tests -- the last three in debug and release, because a lint or a cfg can differ between the two and what ships is release. The targets sit beside the DEST rule rather than replacing it: nothing in the image depends on them, so they run on a workstation without a DEST. Two deviations from sonic-dash-ha's Makefile, both deliberate. It runs `cargo clean` between the debug and release passes; that is a disk-space measure for its agent pool, and here it would throw away a contributor's build cache on every run, so it is omitted -- the two use separate target directories. And ci-doc passes --no-deps, so the verdict is about this platform's documentation rather than its dependencies'. The rest of the diff is rustfmt output over hand-formatted code: re-wrapping, the trailing commas rustfmt adds when it breaks a literal, and the mod/use reordering reorder_imports asks for. No behaviour changes; clippy is clean and the 224 tests here and the 17 in hw-management-rs pass in debug and release. Signed-off-by: ganglyu <glv@nvidia.com>
Collaborator
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why I did it
thermalctldpolls thermal and fan sensors in thepmoncontainer and writes the results to STATE_DB. In Python it importssonic_platform, which loads the vendor plugin at startup and keeps a full interpreter resident for the life of the container. The Mellanox plugin reads hw-management sysfs — pure file I/O, no native-library requirement — so a Rust implementation does the same work with a fraction of the memory: 3.3 MB of anonymous memory against 23.3 MB, measured on the same device.Nothing is switched on for a platform that does not opt in. The Python
thermalctldstays installed and stays the default; which one runs is a key inpmon_daemon_control.jsonand a pmon restart, with no rebuild.Work item tracking
PR:
Both must merge and the submodules advance before this builds. No submodule pointer is moved here.
How I did it
The daemon and the vendor-neutral traits it compiles against live in the two submodules above. This PR adds the build glue and the two vendor crates:
platform/mellanox/mlnx-platform-api-rs— the Mellanox platform API, sitting next to the Pythonmlnx-platform-apirather than undersrc/, because it is vendor code. Portsdevice_data.py,fan.py,fan_drawer.py,thermal.py, PSU and PDB, the fan-drawer LEDs, the SPC1 pre-feed step, liquid-cooling and leak sensors, the SmartSwitch DPU feed andThermalUpdater.platform/mellanox/hw-management-rs— the writer half of hw-management's independent-mode interface, a port ofhw_management_independent_mode_update.pyand the DPU thermal helpers. hw-management publishes its file layout to Python only, so this is a copy of that layout rather than a call into it.rules/sonic-thermalctld-rs.{mk,dep}— the package. Names no vendor and no platform; on a platform that does not opt in it is never built and never enters the pmon image.A vendor opts in from its own
rules.mk, and these two lines are the whole coupling:The vendor crate is compiled from source into the daemon — no prebuilt library, no ABI boundary.
mlnx-platform-api-rs.mkalso adds aSONIC_MAKE_FILEStarget so the crate can be built and unit-tested on its own, and appends it to$(DOCKER_PLATFORM_MONITOR)_FILES, so an ordinary pmon build runs the vendor tests too.Behaviour matches the Python daemon throughout: per-component polling intervals from
platform.json; ASIC, module and DPU feed cadences fromtc_config.json'sdev_parameterswith the same regex semantics; fan drawer LEDs including the capability file and the_blinksuffix;TEMPERATURE_INFO_{slot}on CHASSIS_STATE_DB rather than STATE_DB; leak detection on its own interval; Switch-BMC mirroring on the host and event-log teeing on the BMC; liquid-cooled platforms running no ASIC/module feed and never writingsuspend.One deliberate difference: the release profile sets
panic = "abort", so nothing unwinds andDropnever runs. A panic hook clears the thermal data and suspendshw-management-tcinstead, chaining to the previous hook so the message is still logged. Python's main loop has notry/finallyand skips the suspend write on an unhandled exception, leaving tc running against cleared data.How to verify it
On an opted-in platform, set
thermalctld-rsinpmon_daemon_control.json, restart pmon, and compareTEMPERATURE_INFO,FAN_INFO,FAN_DRAWER_INFOandPHYSICAL_ENTITY_INFOagainst the Python daemon — key counts and field values are identical. Platforms that do not opt in are unaffected: the package is not built and pmon is unchanged.Which release branch to backport (provide reason below if selected)
N/A — new feature, no backport requested.
Tested branch
Description for the changelog
Add
thermalctld-rs, a Rust thermal daemon with a native Mellanox platform API; the Pythonthermalctldstays installed and stays the default.Link to config_db schema for YANG module changes
N/A — no CONFIG_DB schema change.