Skip to content

The owned-CUDA capability floor is env-only with no probe and no docs — a supported machine refuses until the operator guesses two variable names #13

Description

@Qiiks

The owned-CUDA capability floor is read exclusively from environment variables that nothing produces:

// crates/synapse-module/src/lib.rs:5925
fn owned_cuda_floor_decision() -> CudaFloorDecision {
    let driver_api = ["SYNAPSE_CUDA_DRIVER_API", "CUDA_DRIVER_API"] ...
    let compute = ["SYNAPSE_CUDA_COMPUTE_CAPABILITY", "CUDA_COMPUTE_CAPABILITY"] ...
    let (Some(driver_api), Some((major, minor))) = (driver_api, compute) else {
        return CudaFloorDecision::Unsupported {
            reason: CudaUnsupportedReason::HardwareUnavailable, observed: None,
        };
    };

If either variable is unset, the decision is HardwareUnavailablebackend_unavailable before any worker is spawned, with no observed values in the refusal. There is no probe anywhere that fills these in, so a machine that comfortably passes the floor (RTX 4050, CC 8.9, driver API 13030 here) refuses every owned-CUDA load until the operator finds the variable names in source. They are documented nowhere: SYNAPSE_CUDA_DRIVER_API appears in exactly one place (the function above), and neither name appears in README or docs.

Two papercuts observed while setting this up

  1. The driver value is the CUDA driver-API integer, not the marketing version. This box's NVIDIA driver is 610.88, but the floor compares against OWNED_CUDA_MINIMUM_DRIVER_API = 12040, so the correct value is 13030 (cuDriverGetVersion() — verified by calling it directly). Nothing in the env-var name or the refusal says which of the two numbers is expected; an operator who sets 610.88 silently parses to None (it is not a u32) and gets the same HardwareUnavailable as setting nothing.
  2. The refusal drops the observed hardware. HardwareUnavailable carries observed: None, so the error cannot say "your CC is 8.9, the floor is 7.5, but the driver API was missing" — it cannot distinguish "no hardware" from "hardware fine, env incomplete".

Why this is a fixable gap, not an inherent one

The engine already talks to the CUDA driver directly (cuInit, cuCtxGetDevice, cuDevicePrimaryCtxRetain in crates/synapse-engine-cuda/src/cuda.rs), and device_meets_floor(driver_api, major, minor) already exists in synapse-engine-cuda/src/lib.rs:188. The two missing reads are cuDriverGetVersion and cuDeviceGetAttribute(..., COMPUTE_CAPABILITY_MAJOR/MINOR) — the same FFI pattern, a handful of lines.

Since the module deliberately does not link the CUDA engine (the worker is a separate process), the probe probably belongs on the worker side: the worker already reports machine facts in PONG (rss_mb, models_loaded), and the module could fold a worker-reported driver/CC pair into the floor decision (or at minimum into the refusal's observed). Design choice yours; the issue is that today there is no path at all.

Proposed behavior

  • Keep env vars as an override.
  • When unset, probe: cuDriverGetVersion + device attributes, and cache the result per process.
  • Unsupported refusals carry observed whenever the probe ran, so the message distinguishes below-floor from missing-input.
  • Document both variables (and the API-integer format) in README next to the CUDA section.

Happy to implement if the shape above looks right — I can't run the CUDA build gate locally, so I'd want your steer on whether the probe lives in the worker or the module before writing it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions