NAME
Rex::GPU::NVIDIA::Requirement - What NVIDIA driver a GPU generation needs (experimental)
VERSION
version 0.002
SYNOPSIS
use Rex::GPU::NVIDIA::Requirement;
my $req = Rex::GPU::NVIDIA::Requirement->from_gpu($gpu);
say $req->generation // 'unknown generation', ': ', $req->describe;
$req->satisfied_by({ kernel_module => 'open', branch => 580 })
or die "the -open 580 driver cannot drive this GPU\n";
# several GPUs, one driver
my $all = Rex::GPU::NVIDIA::Requirement->intersect(
map { Rex::GPU::NVIDIA::Requirement->from_gpu($_) } @compute_gpus
);
DESCRIPTION
Experimental. This API may change without a deprecation cycle for one release. "install_driver" in Rex::GPU::NVIDIA chooses the driver with it: the Rex::GPU::NVIDIA::Setup classes intersect the requirements of every GPU they install for and take the first of their driver sources that "satisfied_by" accepts. "open_kernel_module_required" in Rex::GPU::Detect and "legacy_driver_requirement" in Rex::GPU::Detect read from it too.
A requirement says which NVIDIA driver a GPU can work with: the kernel module ("kernel_module") and the range of driver branches ("min_branch".."max_branch"). It is keyed on the PCI device ID, the one signal lspci -nn prints even when the host's pci.ids predates the silicon. Objects are immutable.
The table in "generations" is a method, not a global, so a subclass can add a row for silicon this release does not know yet without touching the module.
generation
A label for the GPU generation (Blackwell, Maxwell/Pascal/Volta, ...) taken from "generations", for messages. undef for a device ID the table does not know, and for a requirement built by "intersect" from several GPUs.
kernel_module
Which NVIDIA kernel module the GPU binds with: open (NVIDIA's open GPU kernel modules only — Blackwell has no proprietary module), proprietary (the closed module only — pre-Turing silicon lacks the GSP the open module needs) or either. Defaults to either.
min_branch
The oldest driver branch (an integer, e.g. 570) that supports the GPU, or undef for no lower bound.
max_branch
The newest driver branch that still supports the GPU (e.g. 580 for Maxwell/Pascal/Volta, whose support ends there), or undef for no upper bound.
device_id
The lowercase PCI device ID (the XXXX in [10de:XXXX]) the requirement was looked up for, or undef (no or malformed ID, or an intersected requirement).
name
The GPU name from detection ("from_gpu"), for messages only. Never used to decide anything.
members
For a requirement built by "intersect" from several GPUs: an arrayref of the per-GPU requirements it combines, so a message can name every GPU that constrained it. Empty for a per-GPU requirement.
compute
Whether the GPU generation is one to install a driver for, as the "generations" row for "device_id" declares it (karr #45, #54):
1-- a generation a current driver branch supports: every GPU NVIDIA has published with an ID in that row can run CUDA, so Rex::GPU::Detect counts the device as compute whatever its PCI class, its marketing name (GeForce MX, GT, ... included) and whetherlspcicould resolve that name. The built-in table marks Maxwell through Blackwell Ultra.0-- the row says the generation is not to be installed for: the built-in table marks Kepler and older, whose last driver branch (470) the current distributions no longer package. Rex::GPU::Detect reports such a GPU as not compute whatever its PCI class (a class-0302Tesla K80 too, karr #55), with a warning naming the generation and branch.undef-- no row covers the ID (or it is missing or malformed), the row has nocomputekey, or the requirement was built by "intersect". The table has no opinion; Rex::GPU::Detect falls back to its name rules.
generations
my @rows = $class->generations;
The generation table: an ordered list of hashrefs, each covering the inclusive PCI device-ID range first..last (numbers) with a generation label, a kernel_module (default either), optional min_branch/max_branch and an optional compute flag (see "compute"). The first row whose range contains an ID wins, so a narrower row goes before a block it sits in. An ID no row covers gets either with no bounds and no compute verdict. The built-in rows cover every ID from 0000 to 2FFF without a gap, plus the Blackwell Ultra IDs; the Turing-to-Hopper row carries no constraint (either, no bounds), only its label and compute.
Override it in a subclass to add or replace rows; prepend to $self->SUPER::generations to keep the built-in ones:
package My::GPU::Requirement;
use Moo;
extends 'Rex::GPU::NVIDIA::Requirement';
sub generations {
my ( $self ) = @_;
return (
{ generation => 'Hopper (site policy)', first => 0x2330, last => 0x2330,
kernel_module => 'open', min_branch => 575 },
$self->SUPER::generations
);
}
The table chooses a driver, and its compute flags also decide detection: Rex::GPU::Detect asks this class (the built-in table, not a subclass) for "compute" before its name rules (karr #45, #54). The criterion is the generation, not the marketing name: Maxwell, Pascal, Volta, Turing, Ampere, Ada, Hopper, Blackwell and Blackwell Ultra rows are compute => 1, the Kepler-or-older row compute => 0. A subclass that adds or replaces rows changes the driver choice only, not detection.
Sources, all checked 2026-09-23 (Blackwell, pre-Turing) and 2026-09-24 (Turing to Hopper, compute flags):
Blackwell
2900-2FFF(B2002901, GB2002941, GeForce RTX 50xx, RTX PRO Blackwell, GB102E12) and Blackwell Ultra (B3003182, GB30031C2/31C3, listed one by one, not as a block): the supported-GPU table in NVIDIA's open-gpu-kernel-modules README (driver 615.71.09). The last Ada ID there is28F8, and every listed ID from2901to2F58is Blackwell, so an unlisted ID in2900-2FFFis taken as Blackwell too. Open kernel module only; oldest branch 570 (Blackwell; GB10: 580, next item) and 580 (Blackwell Ultra).GB10
2E12(DGX Spark, aarch64) has its own row ahead of the Blackwell block: open kernel module, oldest branch 580, not 570. NVIDIA lists2E12first in driver 580.119.02 (open-gpu-kernel-modules README of tag580.119.02, and the aarch64supportedchipsREADME of that driver); 580.105.08 and every 570/575 release lack it, and so does 590.44.01 (590.48.01 has it). The table counts whole branches, so it cannot say "580.119.02 or newer": a host that installs an older 580 point release is not caught here.Maxwell/Pascal/Volta
1340-1DF6(Tesla M60/M40, P100, P40, P4, V100, V100S, TITAN V, ...): the 580 legacy list of NVIDIA'ssupportedchipsREADME (615.71.09). Proprietary kernel module only; 580 is the last branch. Compute: the entry-level parts in it (GeForce GT 1030, MX110/MX130/MX150, GTX 750 Ti, GTX 9xx) included -- the 580 README lists every one of those IDs as current, Maxwell Gen1 (GM107/GM108) too.Turing, Ampere, Ada, Hopper
1DF7-28FF: NVIDIA's current list (615.71.09) runs from1E02(TITAN RTX) to28F8in this range; no legacy list has an ID above1DF6. No constraint (either, no bounds): the default driver selection. Compute, GeForce MX450/MX550/MX570 and GTX 16xx included.Kepler or older, every ID below
1340(Kepler0FC6-12BA, Fermi and earlier): the 470 and older legacy lists. Proprietary only, nothing newer than 470. Not compute, at any PCI class (the class-0302Tesla K80/K40/K20 included, karr #55): Rex::GPU::Detect skips them with a warning, and Rex::GPU::NVIDIA refuses to install for one passed to it anyway.
for_device_id
my $req = Rex::GPU::NVIDIA::Requirement->for_device_id('1db4');
The requirement for one NVIDIA PCI device ID (the XXXX in [10de:XXXX], any case), looked up in "generations". undef, a malformed ID and an ID no row covers all return a requirement of either with no bounds. Called on a subclass, it returns an object of that subclass and uses its table.
from_gpu
my $req = Rex::GPU::NVIDIA::Requirement->from_gpu($gpu);
The same for a GPU hashref as returned by "detect" in Rex::GPU::Detect: looks up its device_id and carries its name along for messages. Croaks unless $gpu is a hashref.
satisfied_by
$req->satisfied_by({ kernel_module => 'open', branch => 580 }); # 1 or 0
$req->satisfied_by({ kernel_module => 'open', branch_at_least => 590 });
Whether a driver source satisfies this requirement: 1 or 0, the negation of "why_not". A source is a hashref with a kernel_module (open or proprietary) and what is known about its driver branch, one of:
branch-- an integer: the source installs exactly that branch (nvidia-driver-580-server, Debian 12'snvidia-driver535).branch_at_least-- an integer,branchundefined: the source installs the newest branch its repository carries, which is not known before the install but is known to be at least this one (the repository carries it, and a repository does not lose its newest branch). It satisfies a "min_branch" up to that floor and never a "max_branch": a newer branch can appear in the repository at any time and move past the bound.neither -- the branch is not known at all. That satisfies only a requirement with no bounds.
A requirement of either takes any kernel module; otherwise the module must match exactly. An exact branch must lie within "min_branch".."max_branch", both inclusive. A missing kernel_module satisfies only either: an unknown never passes a real constraint. Croaks unless $source is a hashref, or if branch or branch_at_least is not an integer.
why_not
my $reason = $req->why_not($source); # undef if it fits
undef if "satisfied_by" would say 1, otherwise a short reason for messages ("proprietary kernel module, the open one is needed", "branch 590 is newer than 580").
intersect
my $req = Rex::GPU::NVIDIA::Requirement->intersect(@requirements);
my $req = $v100->intersect($b200); # invocant included: croaks here
The one requirement that satisfies all given ones, for a host with several GPUs: one driver has to drive them all. Called on an object, that object is one of the requirements. The kernel module is the one any member insists on (either only if all say either); min_branch is the highest lower bound, max_branch the lowest upper bound. A single requirement comes back unchanged; several give a new object whose "members" lists them (nested intersections flattened) and whose generation, device_id and name are undef.
Croaks with the "conflicts", naming the GPUs on each side, if members need different kernel modules (a V100 needs proprietary, a B200 open) or the bounds leave no branch (one GPU needs at least 590, another at most 580). Also croaks for an empty list or anything that is not a requirement object.
conflicts
my @why = Rex::GPU::NVIDIA::Requirement->conflicts(@requirements);
What "intersect" would croak about, as a list of messages, one per conflict; empty when the requirements can be combined. For a caller that wants to phrase the failure itself.
describe
print $req->describe; # "open kernel module, driver branch 570 or newer"
A short human-readable form of the constraint, for log lines and error messages.
who
print $req->who; # "GV100GL [Tesla V100] (Maxwell/Pascal/Volta, 10de:1db4)"
Who the requirement is for, for messages: the GPU's name, generation and device ID, or for an intersected requirement the "members", comma separated. NVIDIA GPU when nothing is known.
SEE ALSO
Rex::GPU::NVIDIA, Rex::GPU::Detect, https://github.com/NVIDIA/open-gpu-kernel-modules
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/rex-gpu/issues.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.