Repository navigation
Add Intel Gaudi HPU support through libhlml - #528
Merged
Merged
Conversation
New backend (GAUDI_SUPPORT, enabled by default on Linux) that loads libhlml.so, the library behind hl-smi, at runtime, so the Habana SDK headers are not needed to build. - Reports AIP utilization, device memory, SoC clock, chip temperature, power draw and limit, PCIe link (from sysfs) and rx/tx throughput, device architecture (Gaudi2) and memory type (HBM). - Some HLML queries go through the firmware (PCIe throughput takes ~25ms per call and direction), so the dynamic info is sampled by a background thread. After the first synchronous sample only that thread calls HLML, so libhlml thread safety is not assumed. - hlml_device_get_temperature leaks memory on every call (sensors_get_label inside libhlml). The chip temperature is read from the same hwmon channels instead: the "On Chip" sensors listed for the board in /etc/sensors.d/hl_sensors.conf, as HLML does. - HLML does not list processes. Like hl-smi, the backend scans /proc for the /dev/accel/accelN node (matched by st_rdev, so remapped device nodes in containers work). Gaudi runs one compute context per device. The process memory is the value the framework publishes in /dev/shm/mem_usage_accelN, read through the process's own fd, which is what hl-smi reports. Also merge the two copies of the supported-vendor sentence in the README. Tested on 8x Gaudi2 HL-225 with driver 1.24.1: values match hl-smi. Clean under ThreadSanitizer and AddressSanitizer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CMakeLists.txt # README.md
gpuinfo_gaudi_get_device_handles indexed gaudi_devices with the gaudi_device_count left over from a previous enumeration. Calling it twice without an intervening shutdown wrote past the newly allocated array (heap-buffer-overflow under AddressSanitizer) and leaked the previous allocation. Reject the second call like the PPU backend does, and add a gaudiTests test which enumerates twice and then checks that enumeration works again after a shutdown.
The HLML structures are hand-transcribed from the 1.24 SDK because nvtop must build without the Habana headers. Spell out that the output structures are an ABI contract, mark the fields that only exist for the layout, and add compile-time size and offset assertions so that an accidental edit (or a bad merge resolution) is caught at build time. The assertions document the target ABI; they cannot detect a newer libhlml that has grown one of the structures, so the comment asks for a recheck when the SDK bumps its API and for the transcribed block to stay in sync with it.
nvtop transcribes the HLML structures because it must build without the Habana SDK headers. The API has no size negotiation and libhlml writes the size of its own structure into the caller's object, so a newer library that grew a structure would overflow nvtop's stack. The compile-time assertions only protect nvtop's own definitions. Give the utilization and memory output structures a 512-byte reserved tail, assert the transcribed 1.24 prefix offsets, and keep the full-size assertions. Extend gaudiTests with fake entry points that write 256 bytes past the known prefix, emulating a newer library, and verify the values still arrive intact under AddressSanitizer and UBSan.
When the framework did not publish /dev/shm/mem_usage_accelN, a single process on the device was attributed the whole device used memory. That value includes the 768 MiB the device reserves and any non-framework allocations, so it did not match what hl-smi shows and overstated the process usage. Leave the process memory unset (N/A) in that case instead. Add a test that keeps a valid device used_memory present and checks that the per-process field stays invalid, plus the existing shm value path. Update the README wording accordingly.
Gaudi has a single compute context per device, but several processes can hold the compute node after a fork. Requiring exactly one holder hid the per-process utilization in that case. Attribute the device utilization to: - the only process holding the device, or - the only process publishing the framework's shm memory when several processes hold it. Leave it unset when neither rule identifies a single process. Add tests for the four cases and update the README.
HLML does not classify the AIP temperature sensors by their libsensors labels: it uses a hardcoded per-ASIC temp_type map and calls sensors_get_label() only for display, which is where the leak comes from. nvtop uses the vendor config labels as a proxy for the same channels; document that, and note that only the vendor file is read because HLML ignores user relabels in other sensors.d files. Also derive the libsensors PCI chip name from the real BDF instead of the hardcoded "-pci-0", so exact "chip" statements match. Add a unit test for the PCI address conversion and update the README.
populate_static_info() calls HLML from the main thread. It is safe only because nvtop populates static info before the first dynamic refresh, which starts the sampler and becomes the only HLML caller. Spell that out in the file header and next to the function so a future runtime re-population path does not silently call HLML concurrently with the sampler.
The per-device monitored flag was set on every refresh but never cleared, so a device the user stopped monitoring kept being sampled whenever another device triggered a round. Make it a one-shot refresh request: the sampler clears it before sampling the device, and recomputes sample_pending from the remaining requests at the end of the round. Requests that arrive while sampling are either served in the same round if their device has not been visited yet, or trigger another round.
Lower-casing the whole sysfs device_type produced names such as "Gaudi2b" and "Gaudi3 hl-338", and the HLML board name was appended even when the device_type already contained it, producing "Gaudi3 hl-338 HL-338". Map the known ASIC strings to their display names, and only append the board name when the family does not already end with it (ignoring case and separators). Also report Goya, which HLML can enumerate, with DDR4 instead of HBM. Add tests for the mapping, suffix comparison and name composition.
Add gpu_processing_unit_hpu to the shared enum and use it for the Gaudi vendor. The per-device labels (utilization meter, clock, PCIe link, setup window and plot legends) now show HPU instead of GPU. The process table keeps its generic GPU/GPU MEM column names because it can mix vendors. The enum already had PPU as a precedent. Update the ProcessingUnitNames test.
HLML selects the AIP temperature sensors with a hardcoded per-ASIC map, while nvtop identifies them through the vendor config labels. A partial or user-modified config could select only some of them and report the maximum of the wrong set. Compare the selected count with HLML's map (Gaudi2: 8, Gaudi3 OAM/FPGA: 16, Gaudi3 HL-338: 12) and fall back to the HLML temperature call when they differ. Unknown ASICs keep trusting the labels. Add tests for the expected counts and the matching helper, and update the README fallback description.
- Drop hlml_device_get_pci_info() and the PCI ABI structures. pdev now comes from the driver's pci_addr sysfs attribute, with a gaudi-<index> fallback. This removes the largest hand-transcribed structure; pci_addr is the same Linux BDF HLML was reading. - Clear the HLML function pointers on shutdown and on partial init failure, copy dlerror() into a stable buffer, force the board name to be NUL-terminated, and drop the unused has_sample flag. - Parse path_index() from basenames, tolerate a " (deleted)" suffix, and also match shm files by the device's host minor for containers that remap the accel node. - Accept only shared-memory layout version 1 (the known one) and remove the arbitrary 4096-byte upper size bound. - Clamp mem_util_rate to 100 and use hex for the pdev fallback so the format-truncation warning disappears. - Name PyTorch/Paddle as the shm publishers in the code comment and README. The pci_info zero-initialization nit went away with the structure. Goya stays under the Gaudi backend name; the README documents it separately.
- Parse the lm-sensors configuration through a testable helper and check labels, ignore and the AIP channel count match. - Cover path_index() basenames, "(deleted)" suffixes and control nodes. - Exercise gaudi_scan_processes() with a fake proc tree: index match, the single-device/single-shm fallback, no match, and the host-minor fallback for a remapped accel node. - Test the sampler request helpers: taking a request clears it, other pending requests remain for the next round.
Avoid walking past the string when sysfs reports an empty device_type, and zero the HLML output structures so a library that writes an older, shorter structure cannot leave the fields we read indeterminate. Cover the empty device_type path in gaudiTests. Fix the README tense and note that Goya has not been validated on hardware, and mention the per-process utilization in the feature list.
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.
This adds a backend for Intel Gaudi (Habana Labs) HPUs. It is built on Linux by default through the new
GAUDI_SUPPORToption.The backend loads
libhlml.soat runtime. HLML is the management library behindhl-smiand is installed with the habanalabs driver packages. Because the library is loaded at runtime, building nvtop does not need the Habana SDK headers. On machines without thehabanalabsmodule, the backend bails out before callingdlopen.What is reported
Gaudi2 HL-225)Gaudi devices are labeled HPU (
gpu_processing_unit_hpu), like the existing PPU label for T-Head.Design notes
hlml_device_get_pcie_throughputtakes about 25 ms per call and per direction, and the utilization and power calls take 3 to 6 ms each. On an 8-card machine, querying in the UI loop added more than 0.5 s per refresh.refresh_dynamic_infoonly copies the latest sample.-salready have data. After that, only the sampler thread calls HLML, so the backend does not rely on libhlml being thread safe. If the thread cannot be created, the backend falls back to synchronous sampling.hlml_device_get_temperatureleaks memory on every call: about 260 bytes, fromsensors_get_labelinside libhlml, found with ASan. With 8 cards refreshing every second, that is about 180 MB a day.ON_AIPtable. The driver's/etc/sensors.d/hl_sensors.conflabels those channelsOn Chip ...and covers Gaudi 1, 2 and 3.chipblock (honoringignore), then reads those hwmon channels directly. The number of selected channels is checked against HLML's per-ASIC map (Gaudi2: 8, Gaudi3 OAM/FPGA: 16, Gaudi3 HL-338: 12) so a partial or user-modified config falls back to HLML instead of reporting the maximum of the wrong set.hl-smi: it scans/proc/*/fdfor the/dev/accel/accelNcompute node.st_rdevrather than by path, so devices remapped inside containers are attributed correctly.accel_controlD*, which monitoring tools open, is ignored./procis scanned once per refresh, not once per device./dev/shm/mem_usage_accelN. The process keeps that file open, so it is read through/proc/<pid>/fd, which also works across mount namespaces. If the framework does not publish a value, the process memory isN/A; the device-wide used memory is not attributed to the process because it includes the 768 MiB the device reserves.Shared code
Besides the CMake option, the README section and the new test, the shared code gains the
gpu_processing_unit_hpuenum value (following thePPUprecedent) and its name mapping. Device enumeration also rejects a second call instead of indexing the previous device array, matching the PPU backend.Testing
Tested on 8x Gaudi2 HL-225, habanalabs driver 1.24.1, Ubuntu 24.04 (kernel 6.17), while vLLM was serving on all cards:
hl-smiat the same time, nvtop shows the same per-process memory to the MiB, the same device memory, and the same power and temperature within the noise between two reads.-s/-lsnapshots includedevice_architecture: Gaudi2andmemory_type: HBM.nvtop -l -d 1for 60 s): no reports.hlml_init/hlml_shutdowninside libhlml, and it is the same after 15 s and after 60 s.-Wall -Wextra. The new files are formatted with the repository's.clang-format.The new
gaudiTeststarget runs in CI and covers the libhlml ABI growth padding, double enumeration, device-name composition, AIP sensor count validation, lm-sensors config parsing, PCI chip address conversion,/procscanning with a fake tree (including container remapping), process memory/utilization attribution, and the sampler request helpers. It passes under AddressSanitizer/UBSan and ThreadSanitizer.Known differences with
hl-smihl-smi's summary adds the 50 W of the auxiliary 12 V supply, which HLML does not expose.