Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
108 changes: 108 additions & 0 deletions onnxruntime/core/platform/linux/device_discovery.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,94 @@
return Status::OK();
}

// PCI bus-based GPU detection as a fallback for environments where DRM sysfs entries
// are not available (e.g., AKS/Kubernetes containers where the nvidia-drm kernel module
// is not loaded but GPU PCI devices are still exposed via sysfs).

struct GpuPciPathInfo {
fs::path path;
std::string pci_bus_id;
};

Status DetectGpuPciPaths(std::vector<GpuPciPathInfo>& gpu_pci_paths_out) {
std::error_code error_code{};
const fs::path sysfs_pci_devices_path = "/sys/bus/pci/devices";
const bool path_exists = fs::exists(sysfs_pci_devices_path, error_code);
ORT_RETURN_IF_ERROR(ErrorCodeToStatus(error_code));

if (!path_exists) {
gpu_pci_paths_out = {};
return Status::OK();
}

std::vector<GpuPciPathInfo> gpu_pci_paths{};

auto dir_iterator = fs::directory_iterator{sysfs_pci_devices_path, error_code};
ORT_RETURN_IF_ERROR(ErrorCodeToStatus(error_code));

for (const auto& dir_item : dir_iterator) {
const auto& device_path = dir_item.path();

// Read PCI class code to identify GPU devices.
// The class file contains a 24-bit value: 0xCCSSpp (class/subclass/prog-if).
uint32_t pci_class{};
if (auto status = ReadValueFromFile(device_path / "class", pci_class); !status.IsOK()) {
continue;
}

// Check for GPU/display controller PCI class codes:
// Base class 0x03 = Display controller
// Sub-class 0x00 = VGA compatible controller
// Sub-class 0x02 = 3D controller (common for NVIDIA data center/compute GPUs)
Comment thread
edgchen1 marked this conversation as resolved.
const uint8_t base_class = static_cast<uint8_t>((pci_class >> 16) & 0xFF);
const uint8_t sub_class = static_cast<uint8_t>((pci_class >> 8) & 0xFF);
if (base_class != 0x03 || (sub_class != 0x00 && sub_class != 0x02)) {
continue;
}

GpuPciPathInfo path_info{};
path_info.path = device_path;
path_info.pci_bus_id = device_path.filename().string();

Check warning on line 225 in onnxruntime/core/platform/linux/device_discovery.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <string> for string [build/include_what_you_use] [4] Raw Output: onnxruntime/core/platform/linux/device_discovery.cc:225: Add #include <string> for string [build/include_what_you_use] [4]
gpu_pci_paths.emplace_back(std::move(path_info));
}

gpu_pci_paths_out = std::move(gpu_pci_paths);
return Status::OK();
}

Status GetGpuDeviceFromPci(const GpuPciPathInfo& path_info, size_t device_idx, OrtHardwareDevice& gpu_device_out) {
OrtHardwareDevice gpu_device{};
const auto& pci_path = path_info.path;

// vendor id - directly under PCI device path
uint16_t vendor_id{};
ORT_RETURN_IF_ERROR(ReadValueFromFile(pci_path / "vendor", vendor_id));
gpu_device.vendor_id = vendor_id;

// device id - directly under PCI device path
uint16_t device_id{};
ORT_RETURN_IF_ERROR(ReadValueFromFile(pci_path / "device", device_id));
gpu_device.device_id = device_id;

// metadata
// Use "card_idx" key for consistency with DRM-based detection, using device enumeration order.
gpu_device.metadata.Add("card_idx", MakeString(device_idx));
Comment thread
edgchen1 marked this conversation as resolved.
Outdated

if (const auto is_gpu_discrete = IsGpuDiscrete(vendor_id, device_id);
is_gpu_discrete.has_value()) {
gpu_device.metadata.Add("Discrete", (*is_gpu_discrete ? "1" : "0"));
}

if (!path_info.pci_bus_id.empty()) {
gpu_device.metadata.Add("pci_bus_id", path_info.pci_bus_id);
}

gpu_device.type = OrtHardwareDeviceType_GPU;

gpu_device_out = std::move(gpu_device);
return Status::OK();
}

Status GetGpuDevices(std::vector<OrtHardwareDevice>& gpu_devices_out) {
std::vector<GpuSysfsPathInfo> gpu_sysfs_path_infos{};
ORT_RETURN_IF_ERROR(DetectGpuSysfsPaths(gpu_sysfs_path_infos));
Expand All @@ -188,6 +276,26 @@
gpu_devices.emplace_back(std::move(gpu_device));
}

// If DRM-based detection found no GPUs, fall back to PCI bus scanning.
// This handles containerized environments (e.g., AKS/Kubernetes) where the DRM
// subsystem (nvidia-drm) may not be available but GPU PCI devices are still
// exposed via /sys/bus/pci/devices/.
if (gpu_devices.empty()) {
LOGS_DEFAULT(VERBOSE) << "No GPUs found via /sys/class/drm. "
<< "Falling back to PCI bus scanning via /sys/bus/pci/devices/.";

Comment thread
baijumeswani marked this conversation as resolved.
std::vector<GpuPciPathInfo> gpu_pci_path_infos{};
ORT_RETURN_IF_ERROR(DetectGpuPciPaths(gpu_pci_path_infos));

gpu_devices.reserve(gpu_pci_path_infos.size());

for (size_t i = 0; i < gpu_pci_path_infos.size(); ++i) {
OrtHardwareDevice gpu_device{};
ORT_RETURN_IF_ERROR(GetGpuDeviceFromPci(gpu_pci_path_infos[i], i, gpu_device));
gpu_devices.emplace_back(std::move(gpu_device));
}
}

gpu_devices_out = std::move(gpu_devices);
return Status::OK();
}
Expand Down
11 changes: 11 additions & 0 deletions onnxruntime/test/platform/device_discovery_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,16 @@ TEST(DeviceDiscoveryTest, HasCpuDevice) {
#endif // defined(CPUINFO_SUPPORTED)
}

TEST(DeviceDiscoveryTest, GpuDevicesHaveValidProperties) {
const auto gpu_devices = GetDevicesByType(OrtHardwareDeviceType_GPU);

// GPU detection should not crash. If GPUs are present, validate their properties.
for (const auto& gpu_device : gpu_devices) {
EXPECT_EQ(gpu_device.type, OrtHardwareDeviceType_GPU);
Comment thread
edgchen1 marked this conversation as resolved.
Outdated
EXPECT_NE(gpu_device.vendor_id, 0u);
EXPECT_NE(gpu_device.device_id, 0u);
}
Comment thread
baijumeswani marked this conversation as resolved.
}

} // namespace onnxruntime::test
#endif // !defined(ORT_MINIMAL_BUILD) && !defined(_GAMING_XBOX)
Loading