Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ cufile.log
.cache/

# Asio
subprojects/asio-*
subprojects/asio-*

build
subprojects/.wraplock
22 changes: 19 additions & 3 deletions examples/python/expanded_two_peers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,22 @@
import numpy as np
import torch

from nixl._api import nixl_agent, nixl_agent_config
from nixl.logging import get_logger
try:
try:
from nixl._api import nixl_agent, nixl_agent_config
from nixl.logging import get_logger
except ImportError:
from rixl._api import nixl_agent, nixl_agent_config
from rixl.logging import get_logger

logger = get_logger(__name__)
NIXL_AVAILABLE = True
except ImportError:
import logging

logger = get_logger(__name__)
logger = logging.getLogger(__name__)
logger.error("NIXL API missing install NIXL.")
NIXL_AVAILABLE = False


def parse_args():
Expand All @@ -33,6 +45,10 @@ def parse_args():


if __name__ == "__main__":
if not NIXL_AVAILABLE:
logger.warning("Skipping example - NIXL bindings not available")
sys.exit(0)

args = parse_args()

# initiator use default port
Expand Down
9 changes: 5 additions & 4 deletions src/plugins/libfabric/libfabric_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,12 @@ nixlLibfabricEngine::nixlLibfabricEngine(const nixlBackendInitParams *init_param

NIXL_INFO << "System runtime: "
<< (runtime_ == FI_HMEM_CUDA ? "CUDA" :
runtime_ == FI_HMEM_ROCR ? "ROCr" :
runtime_ == FI_HMEM_NEURON ? "NEURON" :
"SYSTEM");

#ifdef HAVE_CUDA
if (runtime_ == FI_HMEM_CUDA) {
if (runtime_ == FI_HMEM_CUDA || runtime_ == FI_HMEM_ROCR) {
// Initialize CUDA context management
vramInitCtx();
// CUDA address workaround
Expand Down Expand Up @@ -673,7 +674,7 @@ nixlLibfabricEngine::getSupportedMems() const {
nixl_mem_list_t mems;
mems.push_back(DRAM_SEG);
#ifdef HAVE_CUDA
if (runtime_ == FI_HMEM_CUDA) {
if (runtime_ == FI_HMEM_CUDA || runtime_ == FI_HMEM_ROCR) {
NIXL_DEBUG << "CUDA runtime detected, adding VRAM support";
mems.push_back(VRAM_SEG);
} else
Expand Down Expand Up @@ -708,7 +709,7 @@ nixlLibfabricEngine::registerMem(const nixlBlobDesc &mem,
// Use system runtime type to determine device-specific operations
if (nixl_mem == VRAM_SEG) {
#ifdef HAVE_CUDA
if (runtime_ == FI_HMEM_CUDA) {
if (runtime_ == FI_HMEM_CUDA || runtime_ == FI_HMEM_ROCR) {
// CUDA-specific address query
// For multi-GPU support, skip CUDA address workaround
if (cuda_addr_wa_) {
Expand Down Expand Up @@ -766,7 +767,7 @@ nixlLibfabricEngine::registerMem(const nixlBlobDesc &mem,

#ifdef HAVE_CUDA
// Set CUDA context before libfabric operations for VRAM
if (nixl_mem == VRAM_SEG && runtime_ == FI_HMEM_CUDA) {
if (nixl_mem == VRAM_SEG && (runtime_ == FI_HMEM_CUDA || runtime_ == FI_HMEM_ROCR)) {
vramApplyCtx();
}
#endif
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/libfabric/libfabric_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,17 @@
#include "libfabric/libfabric_common.h"

#ifdef HAVE_CUDA
#ifdef __HIP_PLATFORM_AMD__
#include <hip/hip_runtime.h>
#include <hip/hip_runtime_api.h>
// Define CUDA types as HIP equivalents for AMD
typedef hipCtx_t CUcontext;
typedef hipDevice_t CUdevice;
#else
#include <cuda.h>
#include <cuda_runtime.h>
#endif
#endif

// Forward declarations
class nixlLibfabricEngine;
Expand Down
12 changes: 10 additions & 2 deletions src/plugins/libfabric/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@

# LibFabric plugin configuration

# Hipify sources that include cuda_runtime.h for ROCm/HIP builds
hipify_cmd = find_program('hipify-perl')
hipify_src = generator(hipify_cmd,
output : ['@BASENAME@_hip.cpp'],
arguments : ['@INPUT@', '@EXTRA_ARGS@', '@OUTPUT@'])

libfabric_backend_hip_src = hipify_src.process('libfabric_backend.cpp', extra_args: '-o')

# Enable libfabric utils layer
libfabric_plugin_deps = [
nixl_infra,
Expand All @@ -38,7 +46,7 @@ endif
if 'LIBFABRIC' in static_plugins
libfabric_backend_lib = static_library(
'LIBFABRIC',
'libfabric_backend.cpp',
libfabric_backend_hip_src,
'libfabric_backend.h',
'libfabric_plugin.cpp',
dependencies: libfabric_plugin_deps,
Expand All @@ -50,7 +58,7 @@ if 'LIBFABRIC' in static_plugins
else
libfabric_backend_lib = shared_library(
'LIBFABRIC',
'libfabric_backend.cpp',
libfabric_backend_hip_src,
'libfabric_backend.h',
'libfabric_plugin.cpp',
dependencies: libfabric_plugin_deps,
Expand Down
7 changes: 7 additions & 0 deletions src/utils/libfabric/libfabric_rail.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,13 @@ nixlLibfabricRail::registerMemory(void *buffer,
mr_attr.device.cuda = device_id;
NIXL_DEBUG << "CUDA memory registration - iface: FI_HMEM_CUDA, device.cuda: "
<< device_id;
} else if (iface == FI_HMEM_ROCR) {
// AMD ROCr memory registration
// ROCr uses HSA agent handles for device identification
// The device_id corresponds to the GPU index (0-based)
mr_attr.device.rocr = device_id;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am ok with this here, but its probably something that we will have to address at one point. The order in which devices are presented on the HIP layer and on the HSA layer doesn't necessarily have to match. So the clean solution is to have somewhere some code that performs the matching using the BDF retrieved for a hip device, and identifying the HSA agent with the same BDF

NIXL_DEBUG << "ROCr memory registration - iface: FI_HMEM_ROCR, device.rocr: "
<< device_id;
} else if (iface == FI_HMEM_NEURON) {
/*
* Store a sentinel; libfabric requires this to be initialized.
Expand Down
3 changes: 3 additions & 0 deletions src/utils/libfabric/libfabric_rail_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ nixlLibfabricRailManager::nixlLibfabricRailManager(size_t striping_threshold)
runtime_ = FI_HMEM_CUDA;
NIXL_INFO << "System runtime: CUDA for " << topology->getNumNvidiaAccel()
<< " NVIDIA GPU(s)";
} else if (topology->getNumAmdAccel() > 0) {
runtime_ = FI_HMEM_ROCR;
NIXL_INFO << "System runtime: ROCr for " << topology->getNumAmdAccel() << " AMD GPU(s)";
} else if (topology->getNumAwsAccel() > 0) {
runtime_ = FI_HMEM_NEURON;
NIXL_INFO << "System runtime: NEURON for " << topology->getNumAwsAccel()
Expand Down
4 changes: 4 additions & 0 deletions src/utils/libfabric/libfabric_rail_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@
#include "libfabric_rail.h"

#ifdef HAVE_CUDA
#ifdef __HIP_PLATFORM_AMD__
#include <hip/hip_runtime.h>
#else
#include <cuda.h>
#include <cuda_runtime.h>
#endif
#endif

// Forward declarations
class nixlLibfabricTopology;
Expand Down
63 changes: 49 additions & 14 deletions src/utils/libfabric/libfabric_topology.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@
#include <rdma/fi_domain.h>

#ifdef HAVE_CUDA
#ifdef __HIP_PLATFORM_AMD__
#include <hip/hip_runtime.h>
#else
#include <cuda_runtime.h>
#endif
#endif

nixlLibfabricTopology::nixlLibfabricTopology()
: num_aws_accel(0),
num_nvidia_accel(0),
num_amd_accel(0),
num_numa_nodes(0),
num_devices(0),
topology_discovered(false),
Expand Down Expand Up @@ -95,7 +100,7 @@ nixlLibfabricTopology::discoverTopology() {
buildNicInfoMap();

// Build nVidia accelerator to EFA mapping based on PCIe topology
if (num_nvidia_accel > 0) {
if (num_nvidia_accel > 0 || num_amd_accel > 0) {
status = buildAccelToEfaMapping();
if (status != NIXL_SUCCESS) {
NIXL_ERROR << "Failed to build accelerator to EFA mapping";
Expand Down Expand Up @@ -258,7 +263,9 @@ nixlLibfabricTopology::printTopologyInfo() const {
}
NIXL_TRACE << "=== Libfabric Topology Information ===";
NIXL_TRACE << "Topology discovered: " << (topology_discovered ? "Yes" : "No");
NIXL_TRACE << "Number of AWS accelerators: " << num_aws_accel;
NIXL_TRACE << "Number of NVIDIA accelerators: " << num_nvidia_accel;
NIXL_TRACE << "Number of AMD accelerators: " << num_amd_accel;
NIXL_TRACE << "Number of AWS Neuron accelerators: " << num_aws_accel;
NIXL_TRACE << "Number of NUMA nodes: " << num_numa_nodes;
NIXL_TRACE << "Number of EFA devices: " << num_devices;
NIXL_TRACE << "EFA devices: ";
Expand All @@ -285,7 +292,9 @@ std::string
nixlLibfabricTopology::getTopologyString() const {
std::stringstream ss;
ss << "Libfabric Topology: ";
ss << "AWS_Accelerators=" << num_aws_accel << ", ";
ss << "NVIDIA_GPUs=" << num_nvidia_accel << ", ";
ss << "AMD_GPUs=" << num_amd_accel << ", ";
ss << "Neuron_Accelerators=" << num_aws_accel << ", ";
ss << "NUMA=" << num_numa_nodes << ", ";
ss << "EFA=" << num_devices << ", ";
ss << "Discovered=" << (topology_discovered ? "Yes" : "No");
Expand Down Expand Up @@ -398,29 +407,39 @@ nixl_status_t
nixlLibfabricTopology::discoverAccelWithHwloc() {
num_aws_accel = 0;
num_nvidia_accel = 0;
num_amd_accel = 0;
// Find all PCI devices and log detailed information
static const char *vendor_names[2] = {"NEURON", "NVIDIA"};
hwloc_obj_t pci_obj = nullptr;
while ((pci_obj = hwloc_get_next_pcidev(hwloc_topology, pci_obj)) != nullptr) {
const bool is_nvidia_accel = isNvidiaAccel(pci_obj);
if (is_nvidia_accel || isNeuronAccel(pci_obj)) {
const bool is_neuron_accel = isNeuronAccel(pci_obj);
const bool is_amd_accel = isAmdAccel(pci_obj);

if (is_nvidia_accel || is_neuron_accel || is_amd_accel) {
std::string pcie_addr = getPcieAddressFromHwlocObj(pci_obj);
// Get device and vendor info
uint16_t vendor_id = pci_obj->attr->pcidev.vendor_id;
uint16_t device_id = pci_obj->attr->pcidev.device_id;
uint16_t class_id = pci_obj->attr->pcidev.class_id;

NIXL_TRACE << "Found " << vendor_names[is_nvidia_accel] << " accelerator "
<< num_aws_accel << ": " << pcie_addr << " (vendor=" << std::hex << vendor_id
<< ", device=" << device_id << ", class=" << class_id << std::dec << ")";
const char *vendor_name = is_nvidia_accel ? "NVIDIA" : is_amd_accel ? "AMD" : "NEURON";

NIXL_TRACE << "Found " << vendor_name << " accelerator: " << pcie_addr
<< " (vendor=" << std::hex << vendor_id << ", device=" << device_id
<< ", class=" << class_id << std::dec << ")";

num_aws_accel++;
num_nvidia_accel += is_nvidia_accel;
if (is_nvidia_accel) {
num_nvidia_accel++;
} else if (is_amd_accel) {
num_amd_accel++;
} else {
num_aws_accel++;
}
}
}

NIXL_TRACE << "Discovered " << num_aws_accel << " "
<< vendor_names[num_aws_accel == num_nvidia_accel] << " devices via hwloc";
NIXL_TRACE << "Discovered " << num_nvidia_accel << " NVIDIA, " << num_amd_accel << " AMD, and "
<< num_aws_accel << " Neuron accelerators via hwloc";

// If we found more than 8 NVIDIA accelerators on P5en, investigate further
if (num_nvidia_accel > 8) {
Expand Down Expand Up @@ -564,10 +583,10 @@ nixlLibfabricTopology::buildTopologyAwareGrouping() {
discovered_nics.push_back(entry.second);
}

// Step 2: Discover accelerators
// Step 2: Discover accelerators (NVIDIA and AMD)
hwloc_obj_t pci_obj = nullptr;
while ((pci_obj = hwloc_get_next_pcidev(hwloc_topology, pci_obj)) != nullptr) {
if (isNvidiaAccel(pci_obj)) {
if (isNvidiaAccel(pci_obj) || isAmdAccel(pci_obj)) {
AccelInfo accel;
accel.hwloc_node = pci_obj;
accel.domain_id = pci_obj->attr->pcidev.domain;
Expand Down Expand Up @@ -747,6 +766,22 @@ nixlLibfabricTopology::isNeuronAccel(hwloc_obj_t obj) const {
obj->attr->pcidev.device_id) != std::end(NEURON_DEVICE_IDS);
}

bool
nixlLibfabricTopology::isAmdAccel(hwloc_obj_t obj) const {
if (!obj || obj->type != HWLOC_OBJ_PCI_DEVICE) {
return false;
}
// AMD vendor ID is 0x1002
if (obj->attr->pcidev.vendor_id != 0x1002) {
return false;
}
// Only count devices with GPU class (0x300-0x3ff for display controllers)
// Class 0x302 is 3D controller (GPU), 0x680 is other devices (network, etc.)
// MI300X uses class 0x1200 (Processing accelerators), consumer GPUs use 0x300-0x3ff
uint16_t class_id = obj->attr->pcidev.class_id;
return (class_id >= 0x300 && class_id < 0x400) || (class_id == 0x1200);
}

bool
nixlLibfabricTopology::isEfaDevice(hwloc_obj_t obj) const {
if (!obj || obj->type != HWLOC_OBJ_PCI_DEVICE) {
Expand Down
16 changes: 15 additions & 1 deletion src/utils/libfabric/libfabric_topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class nixlLibfabricTopology {
// System information
int num_aws_accel; // AWS Trainium accelerators
int num_nvidia_accel; // NVIDIA GPU accelerators
int num_amd_accel; // AMD GPU accelerators
int num_numa_nodes;
int num_devices;

Expand Down Expand Up @@ -145,6 +146,8 @@ class nixlLibfabricTopology {
bool
isNeuronAccel(hwloc_obj_t obj) const;
bool
isAmdAccel(hwloc_obj_t obj) const;
bool
isEfaDevice(hwloc_obj_t obj) const;

// retrieves line speed of NIC from map
Expand Down Expand Up @@ -210,6 +213,11 @@ class nixlLibfabricTopology {
return num_nvidia_accel;
}

int
getNumAmdAccel() const {
return num_amd_accel;
}

const std::vector<std::string> &
getAllDevices() const {
return all_devices;
Expand All @@ -231,7 +239,13 @@ class nixlLibfabricTopology {

enum fi_hmem_iface
getMrAttrIface(int device_id) const {
return (device_id < num_nvidia_accel) ? FI_HMEM_CUDA : FI_HMEM_NEURON;
if (device_id < num_nvidia_accel) {
return FI_HMEM_CUDA;
} else if (device_id < num_nvidia_accel + num_amd_accel) {
return FI_HMEM_ROCR;

Copilot AI Feb 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getMrAttrIface() can now return FI_HMEM_ROCR, but nixlLibfabricRail::registerMemory() currently only initializes mr_attr.device.* for FI_HMEM_CUDA and FI_HMEM_NEURON. With FI_HMEM_ROCR the device union will remain at its default value, which may violate libfabric’s expectations for ROCR registrations. Please add explicit handling for FI_HMEM_ROCR (including initializing the appropriate mr_attr.device.rocr field) where memory registration attributes are built.

Suggested change
return FI_HMEM_ROCR;
// NOTE: FI_HMEM_ROCR is not yet fully supported in the memory
// registration path (mr_attr.device.rocr initialization). Until
// that wiring is complete, fall back to system memory.
return FI_HMEM_SYSTEM;

Copilot uses AI. Check for mistakes.
} else {
return FI_HMEM_NEURON;
}
Comment on lines +242 to +248

Copilot AI Feb 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getMrAttrIface() selects the HMEM interface by comparing the provided device_id against (num_nvidia_accel, num_amd_accel). In the current call chain, device_id is passed through from the backend as the runtime’s device ordinal (e.g., CUDA device index), not a global index across vendors; on systems with mixed accelerator vendors this can misclassify an AMD/Neuron device_id=0 as CUDA and register memory with the wrong iface. Consider deriving the iface from the device’s PCI vendor (e.g., via device_pci_bus_id + hwloc lookup) or from a single selected runtime, rather than from device_id ranges.

Suggested change
if (device_id < num_nvidia_accel) {
return FI_HMEM_CUDA;
} else if (device_id < num_nvidia_accel + num_amd_accel) {
return FI_HMEM_ROCR;
} else {
return FI_HMEM_NEURON;
}
// The device_id passed here is a runtime-specific ordinal (e.g., CUDA
// device index) and is not guaranteed to be a global cross-vendor index.
// To avoid misclassifying devices on mixed-vendor systems, only infer
// the iface from vendor counts when exactly one accelerator vendor is
// present. Otherwise, fall back to a generic system HMEM iface.
(void)device_id; // currently unused; kept for API compatibility
int vendor_count = 0;
if (num_nvidia_accel > 0) {
vendor_count++;
}
if (num_amd_accel > 0) {
vendor_count++;
}
if (num_aws_accel > 0) {
vendor_count++;
}
// Single-vendor configurations: preserve legacy behavior by selecting
// the corresponding HMEM iface for all device_ids.
if (vendor_count == 1) {
if (num_nvidia_accel > 0) {
return FI_HMEM_CUDA;
}
if (num_amd_accel > 0) {
return FI_HMEM_ROCR;
}
// num_aws_accel > 0 and no other vendors
return FI_HMEM_NEURON;
}
// Mixed-vendor or no-accelerator configuration: we cannot safely infer
// a vendor-specific iface from device_id. Use a generic system iface
// to avoid registering memory with the wrong accelerator runtime.
return FI_HMEM_SYSTEM;

Copilot uses AI. Check for mistakes.
}

/** @brief Invalid NUMA node id constant. */
Expand Down
18 changes: 13 additions & 5 deletions src/utils/libfabric/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Hipify sources that include cuda_runtime.h for ROCm/HIP builds
hipify_cmd = find_program('hipify-perl')
hipify_src = generator(hipify_cmd,
output : ['@BASENAME@_hip.cpp'],
arguments : ['@INPUT@', '@EXTRA_ARGS@', '@OUTPUT@'])

libfabric_topology_hip_src = hipify_src.process('libfabric_topology.cpp', extra_args: '-o')
libfabric_rail_manager_hip_src = hipify_src.process('libfabric_rail_manager.cpp', extra_args: '-o')

# Source files
libfabric_utils_sources = files(
libfabric_utils_sources = [
'libfabric_rail.cpp',
'libfabric_rail_manager.cpp',
'libfabric_common.cpp',
'libfabric_topology.cpp',
# More implementation files will be added as we create them
)
libfabric_topology_hip_src,
libfabric_rail_manager_hip_src,
]

# Header files
libfabric_utils_headers = files(
Expand Down
Empty file added subprojects/.wraplock
Empty file.
Loading
Loading