agentleFS
ROCmROCm/rocprofiler-systems-skills/skills/library-amd-smi/SKILL.md

library-amd-smi

Skill5 starsChanged 18 days agoMITView on GitHub

AMD SMI C++ library for GPU/CPU/NIC monitoring and management. Use when working with AMD hardware monitoring, GPU temperature, power, memory, clocks, PCIe, XGMI, SDMA (System DMA), AINIC (AI NIC) network interfaces, or any amdsmi.h functions.

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every AMD SMI application must initialize and shut down properly:

    line 32
  2. Distinctive:

    Critical: When a metric is not supported by the hardware/firmware, it returns the maximum value for its type:

    line 873

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

AMD SMI C++ Library

AMD System Management Interface library for monitoring and managing AMD GPUs, CPUs, and AI NICs.

Header and Linking

#include "amd_smi/amdsmi.h"
# Build (GPU-only)
g++ -I/opt/rocm/include source.cc -L/opt/rocm/lib -lamd_smi -o output

# Build with NIC/CPU support (enables amdsmi_get_processor_handles_by_type)
g++ -DENABLE_ESMI_LIB -I/opt/rocm/include source.cc -L/opt/rocm/lib -lamd_smi -o output

# Set LD_LIBRARY_PATH for runtime
export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH

# Test with fake AINIC devices (for development without hardware)
AMDSMI_FAKE_AINIC=1 ./output

Initialization Pattern

Every AMD SMI application must initialize and shut down properly:

amdsmi_status_t ret;

// Initialize for GPUs only
ret = amdsmi_init(AMDSMI_INIT_AMD_GPUS);

// Or for CPUs only
ret = amdsmi_init(AMDSMI_INIT_AMD_CPUS);

// Or for AI NICs only
ret = amdsmi_init(AMDSMI_INIT_AMD_NICS);

// Or for all processors
ret = amdsmi_init(AMDSMI_INIT_ALL_PROCESSORS);

// ... use the library ...

// Clean up - MUST be called
ret = amdsmi_shut_down();

Core Concepts

Handles Hierarchy

Socket → Processor (GPU/CPU/NIC) → Core (for CPUs)
  • Socket handle: Physical hardware socket
  • Processor handle: GPU, CPU, or NIC within a socket (may change between app restarts)
  • Device handles are NOT persistent across processes

Getting Handles

// Get socket count and handles
uint32_t socket_count = 0;
amdsmi_get_socket_handles(&socket_count, nullptr);  // Get count
std::vector<amdsmi_socket_handle> sockets(socket_count);
amdsmi_get_socket_handles(&socket_count, sockets.data());

// Get processor handles for a socket (GPUs only - does NOT return NICs!)
uint32_t device_count = 0;
amdsmi_get_processor_handles(sockets[0], &device_count, nullptr);
std::vector<amdsmi_processor_handle> processors(device_count);
amdsmi_get_processor_handles(sockets[0], &device_count, processors.data());

// Get processors by type (supports ALL processor types including NICs)
processor_type_t type = AMDSMI_PROCESSOR_TYPE_AMD_GPU;
amdsmi_get_processor_handles_by_type(socket, type, nullptr, &count);

Processor Handle Functions Comparison

FunctionReturnsUse Case
amdsmi_get_processor_handles()GPUs onlyLegacy GPU-only code
amdsmi_get_processor_handles_by_type()GPUs, NICs, CPUs, APUsRecommended - supports all processor types

CRITICAL: amdsmi_get_processor_handles() only returns GPU processors. It will NOT return NICs, CPUs, or other processor types even if they exist in the system. For NICs, CPUs, or mixed workloads, you MUST use amdsmi_get_processor_handles_by_type() with the appropriate processor type constant.

// WRONG - will miss NICs!
auto handles = amdsmi_get_processor_handles(socket, &count, nullptr);

// CORRECT - explicitly query for NICs
amdsmi_get_processor_handles_by_type(socket, AMDSMI_PROCESSOR_TYPE_AMD_NIC, nullptr, &count);

// CORRECT - explicitly query for GPUs (preferred over legacy function)
amdsmi_get_processor_handles_by_type(socket, AMDSMI_PROCESSOR_TYPE_AMD_GPU, nullptr, &count);

Processor Types

  • AMDSMI_PROCESSOR_TYPE_AMD_GPU - AMD GPU
  • AMDSMI_PROCESSOR_TYPE_AMD_CPU - AMD CPU
  • AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE - CPU core
  • AMDSMI_PROCESSOR_TYPE_AMD_APU - AMD APU
  • AMDSMI_PROCESSOR_TYPE_AMD_NIC - AI Network Interface Card

Common GPU Queries

Temperature

int64_t temp;
amdsmi_get_temp_metric(processor, AMDSMI_TEMPERATURE_TYPE_EDGE,
                       AMDSMI_TEMP_CURRENT, &temp);

// Temperature types: EDGE, JUNCTION, HOTSPOT, VRAM, HBM_0-3, PLX
// Metrics: CURRENT, MAX, MIN, CRITICAL, EMERGENCY, SHUTDOWN

Power

amdsmi_power_info_t power_info;
amdsmi_get_power_info(processor, &power_info);
// power_info.current_socket_power, average_socket_power, etc.

// Power cap
amdsmi_power_cap_info_t cap_info;
amdsmi_get_power_cap_info(processor, 0, &cap_info);

Memory

// Total memory
uint64_t total;
amdsmi_get_gpu_memory_total(processor, AMDSMI_MEM_TYPE_VRAM, &total);

// Memory usage
uint64_t used;
amdsmi_get_gpu_memory_usage(processor, AMDSMI_MEM_TYPE_VRAM, &used);

// Memory types: VRAM, VIS_VRAM, GTT

Clocks

amdsmi_clk_info_t clk_info;
amdsmi_get_clock_info(processor, AMDSMI_CLK_TYPE_GFX, &clk_info);
// clk_info.clk (current), max_clk, min_clk

// Clock types: GFX, MEM, SOC, SYS, DF, DCEF, VCLK0/1, DCLK0/1, PCIE

GPU Activity/Utilization

amdsmi_engine_usage_t usage;
amdsmi_get_gpu_activity(processor, &usage);
// usage.gfx_activity, umc_activity, mm_activity (percentages)

uint32_t busy_percent;
amdsmi_get_gpu_busy_percent(processor, &busy_percent);

Process List with Resource Usage

// Get processes running on GPU
uint32_t num_processes = 0;
amdsmi_get_gpu_process_list(processor, &num_processes, nullptr);  // Get count

std::vector<amdsmi_proc_info_t> proc_list(num_processes);
amdsmi_get_gpu_process_list(processor, &num_processes, proc_list.data());

for (const auto& proc : proc_list) {
    std::cout << "PID: " << proc.pid << " Name: " << proc.name << std::endl;
    std::cout << "  Memory: " << proc.mem / 1024 << " KB" << std::endl;
    std::cout << "  GTT: " << proc.memory_usage.gtt_mem / 1024 << " KB" << std::endl;
    std::cout << "  VRAM: " << proc.memory_usage.vram_mem / 1024 << " KB" << std::endl;
    std::cout << "  CPU: " << proc.memory_usage.cpu_mem / 1024 << " KB" << std::endl;
    std::cout << "  GFX engine: " << proc.engine_usage.gfx << " ns" << std::endl;
    std::cout << "  ENC engine: " << proc.engine_usage.enc << " ns" << std::endl;
    std::cout << "  SDMA usage: " << proc.sdma_usage << " us" << std::endl;
    std::cout << "  CU occupancy: " << proc.cu_occupancy << std::endl;
    std::cout << "  Evicted time: " << proc.evicted_time << " ms" << std::endl;
}

amdsmi_proc_info_t Structure

typedef struct {
    uint32_t pid;                          // Process ID
    char name[AMDSMI_MAX_STRING_LENGTH];   // Process name
    uint64_t mem;                          // Total memory (bytes)
    struct {
        uint64_t gtt_mem;                  // GTT memory usage (bytes)
        uint64_t cpu_mem;                  // CPU memory usage (bytes)
        uint64_t vram_mem;                 // VRAM memory usage (bytes)
    } memory_usage;
    struct {
        uint64_t gfx;                      // GFX engine usage (nanoseconds)
        uint64_t enc;                      // Encoder engine usage (nanoseconds)
    } engine_usage;
    char container_name[AMDSMI_MAX_STRING_LENGTH];
    uint32_t cu_occupancy;                 // Number of CUs utilized
    uint32_t evicted_time;                 // Queue eviction time (milliseconds)
    uint64_t sdma_usage;                   // SDMA usage (microseconds)
} amdsmi_proc_info_t;

Device Info

// Board info (product name, serial, etc.)
amdsmi_board_info_t board_info;
amdsmi_get_gpu_board_info(processor, &board_info);

// ASIC info
amdsmi_asic_info_t asic_info;
amdsmi_get_gpu_asic_info(processor, &asic_info);

// VRAM info
amdsmi_vram_info_t vram_info;
amdsmi_get_gpu_vram_info(processor, &vram_info);

// UUID
char uuid[AMDSMI_GPU_UUID_SIZE];
amdsmi_get_gpu_device_uuid(processor, nullptr, uuid);

// BDF (Bus:Device.Function)
amdsmi_bdf_t bdf;
amdsmi_get_gpu_device_bdf(processor, &bdf);

PCIe Info

amdsmi_pcie_info_t pcie_info;
amdsmi_get_pcie_info(processor, &pcie_info);
// pcie_info.pcie_static (slot_type, max_width, max_speed)
// pcie_info.pcie_metric (current width, speed, bandwidth)

// PCIe throughput
uint64_t sent, received, max_pkt_sz;
amdsmi_get_gpu_pci_throughput(processor, &sent, &received, &max_pkt_sz);

Fan Speed

int64_t speed;
amdsmi_get_gpu_fan_speed(processor, 0, &speed);

int64_t max_speed;
amdsmi_get_gpu_fan_speed_max(processor, 0, &max_speed);

int64_t rpm;
amdsmi_get_gpu_fan_rpms(processor, 0, &rpm);

Firmware Info

amdsmi_fw_info_t fw_info;
amdsmi_get_fw_info(processor, &fw_info);
// fw_info.fw_list[i].fw_id, fw_version

Common CPU Queries

// CPU socket power
uint32_t power;
amdsmi_get_cpu_socket_power(processor, &power);  // milliwatts

// CPU temperature
uint32_t temp;
amdsmi_get_cpu_socket_temperature(processor, &temp);

// CPU energy
uint64_t energy;
amdsmi_get_cpu_socket_energy(processor, &energy);

// Core boost limit
uint32_t boost;
amdsmi_get_cpu_core_boostlimit(core_processor, &boost);

AI NIC (Network Interface Card) Functions

Build Requirements for NIC Support

# NIC functions require -DENABLE_ESMI_LIB to access amdsmi_get_processor_handles_by_type
g++ -DENABLE_ESMI_LIB -I/opt/rocm/include source.cc -L/opt/rocm/lib -lamd_smi -o output

# Test without hardware using fake AINIC devices
AMDSMI_FAKE_AINIC=1 ./output

NIC Discovery

IMPORTANT: amdsmi_get_processor_handles() does NOT return NIC processors. You MUST use amdsmi_get_processor_handles_by_type() with AMDSMI_PROCESSOR_TYPE_AMD_NIC.

// Initialize with NIC support (or AMDSMI_INIT_ALL_PROCESSORS for mixed workloads)
amdsmi_init(AMDSMI_INIT_AMD_NICS);

// Get socket handles
uint32_t socket_count = 0;
amdsmi_get_socket_handles(&socket_count, nullptr);
std::vector<amdsmi_socket_handle> sockets(socket_count);
amdsmi_get_socket_handles(&socket_count, sockets.data());

// Get NIC processor handles (two-call pattern) - MUST use _by_type for NICs!
uint32_t nic_count = 0;
amdsmi_get_processor_handles_by_type(sockets[0],
                                     AMDSMI_PROCESSOR_TYPE_AMD_NIC,
                                     nullptr, &nic_count);
std::vector<amdsmi_processor_handle> nics(nic_count);
amdsmi_get_processor_handles_by_type(sockets[0],
                                     AMDSMI_PROCESSOR_TYPE_AMD_NIC,
                                     nics.data(), &nic_count);

NIC Data Structures

// NIC Link Type (affinity to GPUs/CPUs)
typedef enum {
    AMDSMI_NIC_LINK_TYPE_UNKNOWN,   // Unknown connection type
    AMDSMI_NIC_LINK_TYPE_PCIE,      // Same PCIe root complex
    AMDSMI_NIC_LINK_TYPE_NUMA,      // Different PCIe but same CPU/NUMA
    AMDSMI_NIC_LINK_TYPE_X_NUMA     // Different CPUs/NUMA nodes
} amdsmi_nic_link_type_t;

// NIC Statistic Entry
typedef struct {
    char name[AMDSMI_MAX_STRING_LENGTH];  // e.g., "rx_rdma_ucast_bytes"
    uint64_t value;                        // Counter value
} amdsmi_nic_stat_t;

// NIC ASIC Information
typedef struct {
    uint16_t vendor_id;
    uint16_t subvendor_id;
    uint16_t device_id;
    uint16_t subsystem_id;
    uint8_t revision;
    char permanent_address[AMDSMI_MAX_STRING_LENGTH];
    char product_name[AMDSMI_MAX_STRING_LENGTH];
    char part_number[AMDSMI_MAX_STRING_LENGTH];
    char serial_number[AMDSMI_MAX_STRING_LENGTH];
    char vendor_name[AMDSMI_MAX_STRING_LENGTH];
} amdsmi_nic_asic_info_t;

// NIC Bus Information
typedef struct {
    amdsmi_bdf_t bdf;                     // PCI Bus:Device.Function
    uint8_t max_pcie_width;               // Maximum PCIe lanes
    uint32_t max_pcie_speed;              // Maximum speed in GT/s
    char pcie_interface_version[AMDSMI_MAX_STRING_LENGTH];
    char slot_type[AMDSMI_MAX_STRING_LENGTH];
} amdsmi_nic_bus_info_t;

// NIC NUMA Information
typedef struct {
    uint8_t node;                         // NUMA node number
    char affinity[AMDSMI_MAX_STRING_LENGTH];  // CPU affinity mask
} amdsmi_nic_numa_info_t;

// NIC Firmware Information
typedef struct {
    char name[AMDSMI_MAX_STRING_LENGTH];
    char version[AMDSMI_MAX_STRING_LENGTH];
} amdsmi_nic_fw_t;

typedef struct {
    uint32_t num_fw;
    amdsmi_nic_fw_t fw[AMDSMI_MAX_NIC_FW];  // Up to 16 firmware components
} amdsmi_nic_fw_info_t;

// NIC Port Information
typedef struct {
    amdsmi_bdf_t bdf;
    uint32_t port_num;
    char type[AMDSMI_MAX_STRING_LENGTH];
    char flavour[AMDSMI_MAX_STRING_LENGTH];
    char netdev[AMDSMI_MAX_STRING_LENGTH];    // e.g., "enp226s0"
    uint8_t ifindex;
    char mac_address[AMDSMI_MAX_STRING_LENGTH];
    uint8_t carrier;                          // Link status (0=down, 1=up)
    uint16_t mtu;                             // Maximum transmission unit
    char link_state[AMDSMI_MAX_STRING_LENGTH];
    uint32_t link_speed;                      // Link speed in Mbps
    uint32_t active_fec;                      // FEC modes bitmask
    char autoneg[AMDSMI_MAX_STRING_LENGTH];
    char pause_autoneg[AMDSMI_MAX_STRING_LENGTH];
    char pause_rx[AMDSMI_MAX_STRING_LENGTH];
    char pause_tx[AMDSMI_MAX_STRING_LENGTH];
} amdsmi_nic_port_t;

typedef struct {
    uint32_t num_ports;
    amdsmi_nic_port_t ports[AMDSMI_MAX_NIC_PORTS];  // Up to 32 ports
} amdsmi_nic_port_info_t;

// NIC Driver Information
typedef struct {
    char name[AMDSMI_MAX_STRING_LENGTH];
    char version[AMDSMI_MAX_STRING_LENGTH];
} amdsmi_nic_driver_info_t;

// NIC RDMA Port Information
typedef struct {
    char netdev[AMDSMI_MAX_STRING_LENGTH];    // Network device name
    char state[AMDSMI_MAX_STRING_LENGTH];     // "ACTIVE", "DOWN", etc.
    uint8_t rdma_port;                        // RDMA port number
    uint16_t max_mtu;
    uint16_t active_mtu;
} amdsmi_nic_rdma_port_info_t;

// NIC RDMA Device Information
typedef struct {
    char rdma_dev[AMDSMI_MAX_STRING_LENGTH];  // e.g., "rdma0"
    char node_guid[AMDSMI_MAX_STRING_LENGTH]; // Global Unique Identifier
    char node_type[AMDSMI_MAX_STRING_LENGTH]; // "CA", "Switch", etc.
    char sys_image_guid[AMDSMI_MAX_STRING_LENGTH];
    char fw_ver[AMDSMI_MAX_STRING_LENGTH];
    uint8_t num_rdma_ports;
    amdsmi_nic_rdma_port_info_t rdma_port_info[AMDSMI_MAX_NIC_PORTS];
} amdsmi_nic_rdma_dev_info_t;

typedef struct {
    uint8_t num_rdma_dev;
    amdsmi_nic_rdma_dev_info_t rdma_dev_info[AMDSMI_MAX_NIC_RDMA_DEV];
} amdsmi_nic_rdma_devices_info_t;

NIC Query Functions

// Get NIC driver information
amdsmi_nic_driver_info_t driver_info;
amdsmi_get_nic_driver_info(nic_handle, &driver_info);
// driver_info.name, driver_info.version

// Get NIC ASIC info (vendor, product, serial)
amdsmi_nic_asic_info_t asic_info;
amdsmi_get_nic_asic_info(nic_handle, &asic_info);
// asic_info.vendor_id, device_id, product_name, serial_number

// Get NIC bus info (PCIe configuration)
amdsmi_nic_bus_info_t bus_info;
amdsmi_get_nic_bus_info(nic_handle, &bus_info);
// bus_info.bdf, max_pcie_width, max_pcie_speed

// Get NIC NUMA affinity
amdsmi_nic_numa_info_t numa_info;
amdsmi_get_nic_numa_info(nic_handle, &numa_info);
// numa_info.node, numa_info.affinity

// Get NIC port information (MAC, link state, speed)
amdsmi_nic_port_info_t port_info;
amdsmi_get_nic_port_info(nic_handle, &port_info);
// port_info.num_ports, port_info.ports[i].mac_address, carrier, link_speed

// Get NIC RDMA device information
amdsmi_nic_rdma_devices_info_t rdma_info;
amdsmi_get_nic_rdma_dev_info(nic_handle, &rdma_info);
// rdma_info.num_rdma_dev, rdma_info.rdma_dev_info[i].rdma_dev

NIC RDMA Port Statistics

The statistics API uses a two-call pattern:

// First call: Get count of available statistics
uint32_t num_stats = 0;
amdsmi_get_nic_rdma_port_statistics(nic_handle, rdma_port_idx,
                                    &num_stats, nullptr);

// Second call: Allocate and retrieve statistics
std::vector<amdsmi_nic_stat_t> stats(num_stats);
amdsmi_get_nic_rdma_port_statistics(nic_handle, rdma_port_idx,
                                    &num_stats, stats.data());

// Process statistics
for (uint32_t i = 0; i < num_stats; i++) {
    std::cout << stats[i].name << ": " << stats[i].value << std::endl;
}

Common NIC Statistics

RDMA port statistics typically include:

Statistic NameDescription
rx_rdma_ucast_bytesUnicast bytes received
rx_rdma_ucast_pktsUnicast packets received
tx_rdma_ucast_bytesUnicast bytes transmitted
tx_rdma_ucast_pktsUnicast packets transmitted
rx_rdma_cnp_pktsCongestion Notification Protocol packets received
tx_rdma_cnp_pktsCongestion Notification Protocol packets transmitted

NIC Constants

#define AMDSMI_MAX_NIC_PORTS        32   // Maximum NIC ports per device
#define AMDSMI_MAX_NIC_RDMA_DEV     32   // Maximum RDMA devices per NIC
#define AMDSMI_MAX_NIC_FW           16   // Maximum firmware components
#define AMDSMI_MAX_STRING_LENGTH    64   // String field length

Fake AINIC Mode (Testing Without Hardware)

Enable fake/mock AINIC devices for testing and development when no real AINIC hardware is present.

Environment Variables

VariableDescriptionDefault
AMDSMI_FAKE_AINICEnable fake mode (1, true, yes)disabled
AMDSMI_FAKE_AINIC_COUNTNumber of fake NICs to create1
AMDSMI_FAKE_AINIC_PORTSNumber of ports per NIC2
# Enable fake mode with defaults (1 NIC, 2 ports)
AMDSMI_FAKE_AINIC=1 ./your_program

# Enable with 2 NICs, 4 ports each
AMDSMI_FAKE_AINIC=1 AMDSMI_FAKE_AINIC_COUNT=2 AMDSMI_FAKE_AINIC_PORTS=4 ./your_program

Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│                           AMD SMI System                                 │
│  src/amd_smi/amd_smi_system.cc                                          │
│                                                                          │
│  populate_amd_ainic_devices()                                           │
│    └─> smi_nic_create_context()                                         │
│    └─> smi_discover_nics()           ◄── Returns fake BDFs              │
│    └─> populate_amd_ainic_device()   ◄── Calls smi_get_nic_* APIs       │
│    └─> Creates AMDSmiAINICDevice                                        │
│    └─> Adds to socket->ainic_processors_                                │
└─────────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         smi_nic Library                                  │
│  src/nic/ai-nic/amdsmi_unified/                                         │
│                                                                          │
│  SmiNicSystem (smi_nic_system.cpp)                                      │
│    └─> Checks AMDSMI_FAKE_AINIC env var                                 │
│    └─> Registers SmiNicSubsystemFake OR SmiNicSubsystemPensando         │
│                                                                          │
│  SmiNicSubsystemFake (smi_nic_fake.cpp)                                 │
│    └─> discover() creates SmiNicFake objects                            │
│    └─> get_nics() returns fake NIC list                                 │
│                                                                          │
│  smi_nic_interface.cpp                                                  │
│    └─> Each API checks is_fake_mode()                                   │
│    └─> Returns fake data from SmiNicFake/SmiNicPortFake                 │
└─────────────────────────────────────────────────────────────────────────┘

Key Implementation Files

FilePurpose
src/nic/ai-nic/amdsmi_unified/inc/smi_nic_fake.hFake class declarations
src/nic/ai-nic/amdsmi_unified/src/smi_nic_fake.cppFake class implementations
src/nic/ai-nic/amdsmi_unified/src/smi_nic_system.cppSubsystem registration
src/nic/ai-nic/amdsmi_unified/src/smi_nic_interface.cppAPI fake mode handling

Class Hierarchy

SmiNicSubsystem (base)
    └─> SmiNicSubsystemFake
            └─> owns vector<unique_ptr<SmiNic>> nics_
                    └─> SmiNicFake : public SmiNic
                            └─> owns vector<SmiNicPortFake> fake_ports_

SmiNicPortFake (standalone)
    └─> owns vector<SmiInfiniBandFake> infiniband_
            └─> owns vector<SmiInfiniBandPortFake> ports_

Implementation Pattern

1. Environment Variable Check:

// smi_nic_fake.cpp
bool smi_nic_fake_mode_enabled() {
    const char* env = std::getenv("AMDSMI_FAKE_AINIC");
    if (!env) return false;
    std::string value = to_lower(env);
    return value == "1" || value == "true" || value == "yes";
}

2. Subsystem Registration:

// smi_nic_system.cpp - SmiNicSystem constructor
if (smi_nic_fake_mode_enabled()) {
    register_subsystem(std::make_unique<SmiNicSubsystemFake>());
} else {
    register_subsystem(std::make_unique<SmiNicSubsystemPensando>());
}

3. API Fake Mode Handling:

// smi_nic_interface.cpp
static bool is_fake_mode() {
    return smi_nic_fake_mode_enabled();
}

static const SmiNicFake* get_fake_nic(SmiNicSystem* nic_system, uint64_t device) {
    if (!is_fake_mode() || !nic_system) return nullptr;
    const SmiNic* nic = nic_system->get_nic_by_bdf(device);
    if (!nic) return nullptr;
    return reinterpret_cast<const SmiNicFake*>(nic);  // Safe in fake mode
}

smi_nic_status_t smi_get_nic_asic_info(smi_nic_ctx_t ctx, uint64_t device,
                                        smi_nic_asic_info_t *info) {
    // ... validation ...

    // Handle fake NIC - MUST come before real sysfs access
    if (const auto* fake_nic = get_fake_nic(nic_system, device)) {
        *info = {};
        info->vendor_id = fake_nic->fake_vendor_id().value_or(0);
        // ... populate all fields ...
        return SMI_NIC_STATUS_SUCCESS;
    }

    // Real hardware path follows...
}

Fake Data Constants

static constexpr uint16_t FAKE_VENDOR_ID = 0x1dd8;     // AMD/Pensando
static constexpr uint16_t FAKE_DEVICE_ID = 0x0008;
static constexpr uint8_t FAKE_PCIE_WIDTH = 16;
static constexpr uint32_t FAKE_PCIE_SPEED = 32;        // Gen5
static constexpr uint32_t FAKE_LINK_SPEED = 200000;    // 200 Gbps
static constexpr uint16_t FAKE_MTU = 9000;

// BDF: 0000:eX:00.0 where X = nic_idx + 0xe0
// MAC: 00:ae:cd:00:XX:YY
// GUID: 0000:0000:00ae:cdXX

Socket/Processor Storage

// include/amd_smi/impl/amd_smi_socket.h
AMDSmiSocket
├── processors_          ← GPUs (returned by get_processors())
├── cpu_processors_      ← CPUs
├── ainic_processors_    ← AMD AIINCs ← Fake NICs go here
├── nic_processors_      ← Broadcom NICs
└── switch_processors_   ← Broadcom switches

// CRITICAL: amdsmi_get_processor_handles() returns ONLY processors_ (GPUs)
// Use amdsmi_get_processor_handles_by_type() for NICs!

Common Implementation Mistakes

MistakeProblemFix
Using dynamic_castProject built with -fno-rttiUse reinterpret_cast with is_fake_mode() guard
Overriding non-virtual methodsvendor_id() etc. are not virtualCreate fake_vendor_id() methods
Missing fake mode in APIAPI reads sysfs, failsAdd if (get_fake_nic(...)) check first
Using amdsmi_get_processor_handles()Returns only GPUsUse amdsmi_get_processor_handles_by_type()
Early return when sysfs missingdiscover_nics() exits earlyRemove/modify early return for fake mode

Implementation Checklist

  • Create smi_nic_fake.h with fake class declarations
  • Create smi_nic_fake.cpp with implementations
  • Modify smi_nic_system.cpp constructor to check env var
  • Modify discover_nics() to not early-return for fake mode
  • Update ALL smi_nic_interface.cpp functions for fake mode:
    • smi_get_nic_driver_info
    • smi_get_nic_asic_info
    • smi_get_nic_bus_info
    • smi_get_nic_numa_info
    • smi_get_nic_port_info
    • smi_get_nic_rdma_dev_info
    • smi_get_nic_port_statistics_count
    • smi_get_nic_port_statistics_list
    • smi_get_nic_vendor_statistics_count
    • smi_get_nic_vendor_statistics_list
    • smi_get_nic_rdma_port_statistics_count
    • smi_get_nic_rdma_port_statistics_list
  • Rebuild: cmake -B build && cmake --build build

Complete NIC Example

#include <iostream>
#include <vector>
#include "amd_smi/amdsmi.h"

int main() {
    // Initialize with NIC support
    amdsmi_status_t ret = amdsmi_init(AMDSMI_INIT_AMD_NICS);
    if (ret != AMDSMI_STATUS_SUCCESS) return 1;

    // Get sockets
    uint32_t socket_count = 0;
    amdsmi_get_socket_handles(&socket_count, nullptr);
    std::vector<amdsmi_socket_handle> sockets(socket_count);
    amdsmi_get_socket_handles(&socket_count, sockets.data());

    // Get NIC handles
    for (uint32_t s = 0; s < socket_count; s++) {
        uint32_t nic_count = 0;
        amdsmi_get_processor_handles_by_type(sockets[s],
                                             AMDSMI_PROCESSOR_TYPE_AMD_NIC,
                                             nullptr, &nic_count);
        std::vector<amdsmi_processor_handle> nics(nic_count);
        amdsmi_get_processor_handles_by_type(sockets[s],
                                             AMDSMI_PROCESSOR_TYPE_AMD_NIC,
                                             nics.data(), &nic_count);

        // Query each NIC
        for (uint32_t i = 0; i < nic_count; i++) {
            // Get ASIC info
            amdsmi_nic_asic_info_t asic_info;
            amdsmi_get_nic_asic_info(nics[i], &asic_info);
            std::cout << "NIC: " << asic_info.product_name << std::endl;

            // Get port info
            amdsmi_nic_port_info_t port_info;
            amdsmi_get_nic_port_info(nics[i], &port_info);
            std::cout << "Ports: " << port_info.num_ports << std::endl;

            for (uint32_t p = 0; p < port_info.num_ports; p++) {
                std::cout << "  Port " << p << ": "
                          << port_info.ports[p].netdev
                          << " MAC: " << port_info.ports[p].mac_address
                          << " Speed: " << port_info.ports[p].link_speed << "Mbps"
                          << " State: " << (port_info.ports[p].carrier ? "UP" : "DOWN")
                          << std::endl;
            }

            // Get RDMA devices
            amdsmi_nic_rdma_devices_info_t rdma_info;
            amdsmi_get_nic_rdma_dev_info(nics[i], &rdma_info);

            for (uint32_t r = 0; r < rdma_info.num_rdma_dev; r++) {
                auto& dev = rdma_info.rdma_dev_info[r];
                std::cout << "  RDMA Device: " << dev.rdma_dev << std::endl;

                // Get statistics for each RDMA port
                for (uint32_t rp = 0; rp < dev.num_rdma_ports; rp++) {
                    uint32_t num_stats = 0;
                    amdsmi_get_nic_rdma_port_statistics(nics[i], rp,
                                                        &num_stats, nullptr);

                    std::vector<amdsmi_nic_stat_t> stats(num_stats);
                    amdsmi_get_nic_rdma_port_statistics(nics[i], rp,
                                                        &num_stats, stats.data());

                    std::cout << "    Port " << rp << " Statistics:" << std::endl;
                    for (uint32_t st = 0; st < num_stats; st++) {
                        std::cout << "      " << stats[st].name
                                  << ": " << stats[st].value << std::endl;
                    }
                }
            }
        }
    }

    amdsmi_shut_down();
    return 0;
}

GPU Control Functions

// Set power cap (in microwatts)
amdsmi_set_power_cap(processor, 0, power_cap_uw);

// Set fan speed (0-255 or use AMDSMI_MAX_FAN_SPEED)
amdsmi_set_gpu_fan_speed(processor, 0, speed);
amdsmi_reset_gpu_fan(processor, 0);  // Return to auto

// Set performance level
amdsmi_set_gpu_perf_level(processor, AMDSMI_DEV_PERF_LEVEL_AUTO);

// Set clock frequency
amdsmi_set_clk_freq(processor, AMDSMI_CLK_TYPE_GFX, freq_bitmask);

Topology Functions

// Get link type between two GPUs
amdsmi_io_link_type_t link_type;
uint64_t hops;
amdsmi_topo_get_link_type(processor1, processor2, &hops, &link_type);

// Check P2P accessibility
bool accessible;
amdsmi_is_P2P_accessible(processor1, processor2, &accessible);

// Get NUMA node
uint32_t numa_node;
amdsmi_topo_get_numa_node_number(processor, &numa_node);

GPU Metrics Structure (amdsmi_gpu_metrics_t)

The amdsmi_gpu_metrics_t structure provides comprehensive GPU telemetry in a single read. This is the most efficient way to get multiple metrics at once.

Reading GPU Metrics

amdsmi_gpu_metrics_t metrics;
amdsmi_status_t ret = amdsmi_get_gpu_metrics_info(processor, &metrics);
if (ret == AMDSMI_STATUS_SUCCESS) {
    // Check version first
    uint16_t version = metrics.common_header.format_revision;

    // Access metrics fields
    uint16_t temp = metrics.temperature_edge;      // Temperature in C
    uint16_t gfx_activity = metrics.average_gfx_activity;  // GFX utilization %
    uint16_t power = metrics.average_socket_power; // Power in W
}

Metrics Table Versions

The structure evolved across versions. Check common_header.format_revision:

VersionKey Additions
v1.0Base: temps, activity, power, clocks, fan, PCIe
v1.1temperature_hbm[], gfx/mem_activity_acc
v1.3voltage_soc/gfx/mem, indep_throttle_status
v1.4vcn_activity[], multi-value clocks (arrays)
v1.5jpeg_activity[], pcie_nak counters
v1.6xcp_stats[] (partition metrics), throttle residencies
v1.7vram_max_bandwidth, gfx_below_host_limit_acc
v1.8Extended xcp_stats fields

Detecting Unsupported Values

Critical: When a metric is not supported by the hardware/firmware, it returns the maximum value for its type:

// Unsupported value markers
constexpr uint16_t UNSUPPORTED_16 = 0xFFFF;      // 65535
constexpr uint32_t UNSUPPORTED_32 = 0xFFFFFFFF;  // 4294967295
constexpr uint64_t UNSUPPORTED_64 = 0xFFFFFFFFFFFFFFFF;

// Helper to check if value is valid
template<typename T>
bool is_metric_supported(T value) {
    return value != std::numeric_limits<T>::max();
}

// Usage example
if (is_metric_supported(metrics.average_gfx_activity)) {
    std::cout << "GFX activity: " << metrics.average_gfx_activity << "%" << std::endl;
} else {
    std::cout << "GFX activity: N/A" << std::endl;
}

XCP Stats vs VCN Activity

The xcp_stats[] array provides per-partition metrics for multi-partition GPUs (MI300X, etc.). However, it may not be supported on all platforms.

// xcp_stats structure (per partition)
struct amdsmi_gpu_xcp_metrics_t {
    uint32_t gfx_busy_inst[AMDSMI_MAX_NUM_XCC];  // GFX utilization per XCC
    uint16_t jpeg_busy[AMDSMI_MAX_NUM_JPEG_ENG_V1];  // JPEG engine %
    uint16_t vcn_busy[AMDSMI_MAX_NUM_VCN];   // VCN (decoder) utilization %
    uint64_t gfx_busy_acc[AMDSMI_MAX_NUM_XCC];  // Accumulated GFX activity
    // v1.7+ additions
    uint64_t gfx_below_host_limit_acc[AMDSMI_MAX_NUM_XCC];
    // v1.8+ additions
    uint64_t gfx_below_host_limit_ppt_acc[AMDSMI_MAX_NUM_XCC];
    uint64_t gfx_below_host_limit_thm_acc[AMDSMI_MAX_NUM_XCC];
};

Important: Both xcp_stats[].vcn_busy[] and vcn_activity[] can return 0xFFFF when unsupported. The same applies to jpeg_busy[] and gfx_busy_inst[] (returns 0xFFFFFFFF).

Fallback strategy - try xcp_stats.vcn_busy first, fall back to vcn_activity:

// Helper to get VCN activity with fallback
uint16_t get_vcn_activity(const amdsmi_gpu_metrics_t& metrics,
                          uint16_t partition, uint16_t vcn_idx) {
    // Try xcp_stats.vcn_busy first (per-partition, more detailed)
    if (partition < metrics.num_partition) {
        uint16_t vcn_busy = metrics.xcp_stats[partition].vcn_busy[vcn_idx];
        if (is_metric_supported(vcn_busy)) {
            return vcn_busy;
        }
    }

    // Fallback to global vcn_activity
    if (vcn_idx < AMDSMI_MAX_NUM_VCN) {
        uint16_t vcn_act = metrics.vcn_activity[vcn_idx];
        if (is_metric_supported(vcn_act)) {
            return vcn_act;
        }
    }

    return UINT16_MAX;  // Not available
}

// Usage
for (uint16_t p = 0; p < std::max<uint16_t>(metrics.num_partition, 1); p++) {
    for (int v = 0; v < AMDSMI_MAX_NUM_VCN; v++) {
        uint16_t activity = get_vcn_activity(metrics, p, v);
        if (is_metric_supported(activity)) {
            std::cout << "VCN" << v << " activity: " << activity << "%" << std::endl;
        }
    }
}

Same pattern applies to GFX activity:

// xcp_stats.gfx_busy_inst[] (uint32_t) - per XCC, per partition
// Falls back to average_gfx_activity (uint16_t) - global average

uint32_t get_gfx_activity(const amdsmi_gpu_metrics_t& metrics,
                          uint16_t partition, uint16_t xcc_idx) {
    if (partition < metrics.num_partition) {
        uint32_t gfx = metrics.xcp_stats[partition].gfx_busy_inst[xcc_idx];
        if (is_metric_supported(gfx)) {
            return gfx;
        }
    }
    // Fallback to global average
    if (is_metric_supported(metrics.average_gfx_activity)) {
        return metrics.average_gfx_activity;
    }
    return UINT32_MAX;
}

Key Metrics Fields

FieldTypeDescription
temperature_edgeuint16_tEdge temperature (C)
temperature_hotspotuint16_tHotspot/junction temp (C)
temperature_memuint16_tMemory temperature (C)
temperature_hbm[]uint16_t[4]HBM temperatures (C)
average_gfx_activityuint16_tGFX engine utilization (%)
average_umc_activityuint16_tMemory controller utilization (%)
average_mm_activityuint16_tMultimedia (UVD/VCN) utilization (%)
vcn_activity[]uint16_t[4]Per-VCN utilization (%)
jpeg_activity[]uint16_t[32]Per-JPEG engine utilization (%)
average_socket_poweruint16_tAverage power (W)
current_socket_poweruint16_tInstantaneous power (W)
energy_accumulatoruint64_tEnergy counter (15.259uJ per tick)
current_gfxclkuint16_tCurrent GFX clock (MHz)
current_gfxclks[]uint16_t[8]Per-XCC GFX clocks (v1.4+)
current_uclkuint16_tCurrent memory clock (MHz)
throttle_statusuint32_tThrottle reason bitmask
pcie_link_widthuint16_tPCIe lanes in use
pcie_link_speeduint16_tPCIe speed (0.1 GT/s units)
xgmi_link_widthuint16_tXGMI width (GB/s)
num_partitionuint16_tNumber of active partitions
xcp_stats[]struct[8]Per-partition metrics (v1.6+)

SDMA (System Direct Memory Access)

SDMA engines handle high-speed data transfers on AMD GPUs. AMD GPUs can have up to 8 SDMA engines (SDMA0-SDMA7) plus thread handlers for coordinating DMA operations.

SDMA Firmware IDs

// Firmware block IDs for SDMA engines (in amdsmi_fw_block_t)
AMDSMI_FW_ID_SDMA0,   // System Direct Memory Access 0 (high speed data transfers)
AMDSMI_FW_ID_SDMA1,   // System Direct Memory Access 1 (high speed data transfers)
AMDSMI_FW_ID_SDMA2,   // System Direct Memory Access 2 (high speed data transfers)
AMDSMI_FW_ID_SDMA3,   // System Direct Memory Access 3 (high speed data transfers)
AMDSMI_FW_ID_SDMA4,   // System Direct Memory Access 4 (high speed data transfers)
AMDSMI_FW_ID_SDMA5,   // System Direct Memory Access 5 (high speed data transfers)
AMDSMI_FW_ID_SDMA6,   // System Direct Memory Access 6 (high speed data transfers)
AMDSMI_FW_ID_SDMA7,   // System Direct Memory Access 7 (high speed data transfers)
AMDSMI_FW_ID_SDMA_TH0,  // System Direct Memory Access - Thread Handler 0
AMDSMI_FW_ID_SDMA_TH1,  // System Direct Memory Access - Thread Handler 1

SDMA GPU Block

// GPU block identifier for RAS (Reliability, Availability, Serviceability)
AMDSMI_GPU_BLOCK_SDMA = (1ULL << 1),  // SDMA block

SDMA Per-Process Usage

SDMA usage per process is available through amdsmi_get_gpu_process_list():

// Get processes and their SDMA usage
uint32_t num_processes = 0;
amdsmi_get_gpu_process_list(processor, &num_processes, nullptr);

std::vector<amdsmi_proc_info_t> proc_list(num_processes);
amdsmi_get_gpu_process_list(processor, &num_processes, proc_list.data());

for (const auto& proc : proc_list) {
    // SDMA usage is in microseconds
    std::cout << "PID " << proc.pid << " SDMA usage: "
              << proc.sdma_usage << " us" << std::endl;
}

The sdma_usage field in amdsmi_proc_info_t tracks cumulative SDMA engine utilization per process in microseconds.

Getting SDMA Firmware Version

amdsmi_fw_info_t fw_info;
amdsmi_get_fw_info(processor, &fw_info);

for (uint32_t i = 0; i < fw_info.num_fw_info; i++) {
    switch (fw_info.fw_list[i].fw_id) {
        case AMDSMI_FW_ID_SDMA0:
        case AMDSMI_FW_ID_SDMA1:
        case AMDSMI_FW_ID_SDMA2:
        case AMDSMI_FW_ID_SDMA3:
        case AMDSMI_FW_ID_SDMA4:
        case AMDSMI_FW_ID_SDMA5:
        case AMDSMI_FW_ID_SDMA6:
        case AMDSMI_FW_ID_SDMA7:
            std::cout << "SDMA" << (fw_info.fw_list[i].fw_id - AMDSMI_FW_ID_SDMA0)
                      << " FW version: " << fw_info.fw_list[i].fw_version
                      << std::endl;
            break;
        case AMDSMI_FW_ID_SDMA_TH0:
        case AMDSMI_FW_ID_SDMA_TH1:
            std::cout << "SDMA Thread Handler "
                      << (fw_info.fw_list[i].fw_id - AMDSMI_FW_ID_SDMA_TH0)
                      << " FW version: " << fw_info.fw_list[i].fw_version
                      << std::endl;
            break;
        default:
            break;
    }
}

Error Handling

amdsmi_status_t ret = amdsmi_some_function(...);
if (ret != AMDSMI_STATUS_SUCCESS) {
    const char* err_str;
    amdsmi_status_code_to_string(ret, &err_str);
    std::cerr << "Error: " << err_str << std::endl;
}

Common Status Codes

  • AMDSMI_STATUS_SUCCESS - Operation successful
  • AMDSMI_STATUS_NOT_SUPPORTED - Feature not supported
  • AMDSMI_STATUS_NOT_INIT - Library not initialized
  • AMDSMI_STATUS_INVAL - Invalid arguments
  • AMDSMI_STATUS_NO_PERM - Permission denied
  • AMDSMI_STATUS_NOT_FOUND - Device/resource not found

Environment Variables

  • AMDSMI_GPU_METRICS_CACHE_MS - GPU metrics cache duration (default: 1ms, 0 to disable)
  • AMDSMI_ASIC_INFO_CACHE_MS - ASIC info cache duration (default: 10000ms)

Complete Example (GPU + CPU + NIC)

#include <iostream>
#include <vector>
#include "amd_smi/amdsmi.h"

int main() {
    // Initialize all processor types
    amdsmi_status_t ret = amdsmi_init(AMDSMI_INIT_ALL_PROCESSORS);
    if (ret != AMDSMI_STATUS_SUCCESS) return 1;

    uint32_t socket_count = 0;
    amdsmi_get_socket_handles(&socket_count, nullptr);
    std::vector<amdsmi_socket_handle> sockets(socket_count);
    amdsmi_get_socket_handles(&socket_count, sockets.data());

    for (uint32_t i = 0; i < socket_count; i++) {
        std::cout << "=== Socket " << i << " ===" << std::endl;

        // Query GPUs
        uint32_t gpu_count = 0;
        amdsmi_get_processor_handles_by_type(sockets[i],
                                             AMDSMI_PROCESSOR_TYPE_AMD_GPU,
                                             nullptr, &gpu_count);
        std::vector<amdsmi_processor_handle> gpus(gpu_count);
        amdsmi_get_processor_handles_by_type(sockets[i],
                                             AMDSMI_PROCESSOR_TYPE_AMD_GPU,
                                             gpus.data(), &gpu_count);

        for (uint32_t j = 0; j < gpu_count; j++) {
            amdsmi_board_info_t board_info;
            amdsmi_get_gpu_board_info(gpus[j], &board_info);
            std::cout << "GPU: " << board_info.product_name << std::endl;

            int64_t temp;
            amdsmi_get_temp_metric(gpus[j], AMDSMI_TEMPERATURE_TYPE_EDGE,
                                   AMDSMI_TEMP_CURRENT, &temp);
            std::cout << "  Temperature: " << temp << "C" << std::endl;

            uint64_t vram_used, vram_total;
            amdsmi_get_gpu_memory_total(gpus[j], AMDSMI_MEM_TYPE_VRAM, &vram_total);
            amdsmi_get_gpu_memory_usage(gpus[j], AMDSMI_MEM_TYPE_VRAM, &vram_used);
            std::cout << "  VRAM: " << vram_used / (1024*1024) << "/"
                      << vram_total / (1024*1024) << " MB" << std::endl;
        }

        // Query NICs
        uint32_t nic_count = 0;
        amdsmi_get_processor_handles_by_type(sockets[i],
                                             AMDSMI_PROCESSOR_TYPE_AMD_NIC,
                                             nullptr, &nic_count);
        std::vector<amdsmi_processor_handle> nics(nic_count);
        amdsmi_get_processor_handles_by_type(sockets[i],
                                             AMDSMI_PROCESSOR_TYPE_AMD_NIC,
                                             nics.data(), &nic_count);

        for (uint32_t j = 0; j < nic_count; j++) {
            amdsmi_nic_asic_info_t nic_info;
            amdsmi_get_nic_asic_info(nics[j], &nic_info);
            std::cout << "NIC: " << nic_info.product_name << std::endl;

            amdsmi_nic_port_info_t port_info;
            amdsmi_get_nic_port_info(nics[j], &port_info);
            std::cout << "  Ports: " << port_info.num_ports << std::endl;
        }
    }

    amdsmi_shut_down();
    return 0;
}

API Reference Links

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.