Skip to content

Qualify NVIDIA GPUs by per-device CUDA compute capability #1195

Description

@joelagnel

Problem

The NVIDIA hardware probe records the global CUDA driver API version from nvmlSystemGetCudaDriverVersion_v2 and copies its major version onto every detected GPU as CudaMajorVersion.

var initialize = GetDelegate<NvmlInitialize>(library, "nvmlInit_v2");
shutdown = GetDelegate<NvmlShutdown>(library, "nvmlShutdown");
var getCount = GetDelegate<NvmlDeviceGetCount>(library, "nvmlDeviceGetCount_v2");
var getHandle = GetDelegate<NvmlDeviceGetHandleByIndex>(library, "nvmlDeviceGetHandleByIndex_v2");
var getName = GetDelegate<NvmlDeviceGetString>(library, "nvmlDeviceGetName");
var getUuid = GetDelegate<NvmlDeviceGetString>(library, "nvmlDeviceGetUUID");
var getMemory = GetDelegate<NvmlDeviceGetMemoryInfo>(library, "nvmlDeviceGetMemoryInfo");
var getDriver = GetDelegate<NvmlSystemGetString>(library, "nvmlSystemGetDriverVersion");
var getCuda = GetDelegate<NvmlSystemGetCudaDriverVersion>(library, "nvmlSystemGetCudaDriverVersion_v2");
if (initialize() != NvmlSuccess)
return NvmlProbeResult.Empty;
initialized = true;
string? driverVersion = ReadSystemString(getDriver, DriverVersionCapacity);
int? cudaMajorVersion = getCuda(out int cudaDriverVersion) == NvmlSuccess && cudaDriverVersion > 0
? cudaDriverVersion / 1000
: null;
if (getCount(out uint count) != NvmlSuccess)
return new NvmlProbeResult([], driverVersion, cudaMajorVersion);
var devices = new List<NvmlGpuSnapshot>();
for (uint index = 0; index < count; index++)
{
if (getHandle(index, out IntPtr device) != NvmlSuccess ||
getMemory(device, out NvmlMemory memory) != NvmlSuccess ||
memory.Total == 0)
{
continue;
}
string? name = ReadDeviceString(device, getName, DeviceNameCapacity);
if (string.IsNullOrWhiteSpace(name))

Eligibility then compares that global driver value with the runtime CUDA version and reports CudaCapabilityTooLow:

if (!Version.TryParse(gpu.DriverVersion, out Version? driverVersion) ||
driverVersion < MinimumNvidiaDriverVersion)
{
return UnsupportedCandidate(
gpu,
LocalInferenceEligibilityFailureCode.DriverTooOld,
totalMemoryBytes,
freeMemoryBytes);
}
if (gpu.CudaMajorVersion < runtime.CudaVersion.Major)
{
return UnsupportedCandidate(
gpu,
LocalInferenceEligibilityFailureCode.CudaCapabilityTooLow,
totalMemoryBytes,
freeMemoryBytes);
}

The CUDA driver API version and a GPU's compute capability are different facts. The current code does not call the per-device nvmlDeviceGetCudaComputeCapability API and therefore does not prove that each selected GPU architecture is supported by the pinned llama-server CUDA build.

NVIDIA documents the per-device query here:

https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html

Impact

A sufficiently new installed driver can satisfy the global CUDA-version check without establishing that a particular GPU is compatible with the architecture set supported by the pinned runtime artifacts. On mixed-generation systems, eligibility can select a device without explicit per-device compatibility evidence and discover the incompatibility only after downloading and launching the runtime.

Expected behavior

  • Record the CUDA driver API version separately and name it accordingly.
  • Probe compute-capability major and minor for every NVML device.
  • Define the minimum or supported compute capabilities for each pinned runtime variant.
  • Require both a compatible driver and compatible selected device.
  • Fail closed when required per-device capability evidence is unavailable.
  • Keep selection and launch pinned to the same stable GPU UUID.

Tests

Add coverage for:

  • Compatible driver and compatible GPU
  • Compatible driver but unsupported GPU compute capability
  • Mixed-generation multiple-GPU selection
  • Missing or unsupported NVML compute-capability query
  • Clear separation of driver-version and device-capability failure reasons

Tracking requested by @joelagnel.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:not-repro-on-mainClawSweeper found high-confidence evidence that this issue no longer reproduces on main.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦪 silver shellfishThin issue quality; more reproduction proof or environment detail is needed.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions