Skip to content

fix(examine): identify AMD GPUs by device id and one name table, and never downgrade an APU verdict (ROCMAI-534) - #521

Open
rominf wants to merge 6 commits into
mainfrom
fix-amd-apu-classification
Open

rominf wants to merge 6 commits into
mainfrom
fix-amd-apu-classification

Conversation

@rominf

@rominf rominf commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator
  • If this PR fixes a bug, searched tests/e2e-cucumber/expectations.toml for the fixed ticket ID and removed/narrowed any now-stale xfail rows. — no rows reference this area.
  • If this PR adds a new subcommand or subsystem, its domain implementation lives in its own file per docs/architecture.md. — N/A.
  • Every new or changed user-facing message was read against the code path that runs after it, and its test asserts the resulting state — not only the wording, per AGENTS.md §3. — no message changes; examine-29 asserts the reported state.

Fixes #520

Rebuilt on main (2026-10-26)

This PR was rebuilt from scratch on 1c33248c, because most of what it originally did has landed on main another way:

What is left is the part of #520 that main still gets wrong.

What changes

  • The Linux PCI scan reads the device id. lspci -nn prints [1002:xxxx] on every line, and the scan ignored it. A GPU that pci.ids cannot name (it prints Device) therefore got no target, plus the "not an APU" default. The scan now asks the device-id table first, the same way the Windows probe asks the PNP id.

  • One name table. examine kept its own marketing-name table next to host_gpu's, and the two disagreed:

    • examine's called Krackan Point gfx1150. The device id and the 860M/840M it sells as both say gfx1152, which is a separate TheRock family.
    • APU_KEYWORDS said "APU" without ever naming a target, so a Renoir iGPU came back with an empty gfx_target.

    Both are deleted. examine now asks host_gpu's table, which gains the codenames lspci prints: Strix Halo, Ryzen AI Max, Strix Point, Krackan, Phoenix1–4, Hawk Point, Rembrandt, Raphael, Mendocino, VanGogh/Custom GPU 0405, Renoir, Cezanne, Lucienne and Barcelo.

  • Raven Ridge (gfx902), Picasso/Raven2 (gfx909) and Mendocino (gfx1037) are integrated. No lookup table produces these targets; they arrive from KFD's packed gfx_target_version.

  • Better evidence never produces a worse answer. A rocminfo agent or hipInfo line used to overwrite is_apu with gfx_is_apu_family, which answers false for a discrete card and for a target it has never heard of.

    • The new gfx_apu_verdict returns Some(true), Some(false) or None. DISCRETE_GFX_TARGETS is its "no" half.
    • A target nothing classifies now leaves the earlier verdict standing.
    • For an agent with no PCI row, the verdict falls back to the agent's marketing name, then stays unset.

    This rule is latent until examine: gfx_target is empty for every GPU even though rocminfo lists them → ISA Name: lines overwrite the agent name #393 is fixed: today the parser drops every agent on a real host.

  • The install side keeps ROCMAI-542: detect gfx90c (Renoir/Cezanne Vega iGPUs) and stop mapping it to gfx90X-dcgpu #648's ranking. The name table can now name gfx90c by codename, so the Windows display decoder and preferred_amd_display rank a name-identified gfx90c below every other recognised adapter, just as they already ranked a PCI-identified one. A Vega iGPU listed first therefore cannot hide a discrete card.

Deliberate trade-off

An AMD PCI row that nothing identifies is still reported as discrete (Some(false)), not unknown.

  • The only consumer is fix-9-igpu-dgpu's has_apu && has_discrete_amd.
  • None would silence that check next to every discrete card the tables do not name (RX 5000, Vega, Radeon VII), on the hybrid host it exists for.
  • The cost is a wrong has_discrete_amd on an iGPU-only host whose row nothing identifies. There the check still cannot fire, because has_apu is false.

The trade is documented on classify_amd_marketing_name, and an_unidentifiable_pci_row_is_reported_as_discrete_and_this_is_what_that_costs runs both hosts through diagnose().

Assumptions, stated

  • gfx902/gfx909/gfx1037 are APUs. Source: LLVM's AMDGPU target list (Raven, Raven2, Mendocino), cross-checked with the KFD gfx_target_version values 90002, 90009 and 100307.
  • There is no "Radeon 8045S". The Strix Halo SKU names are 8060S, 8050S and 8040S (8040S: Ryzen AI Max PRO 380). The deleted examine table's 8045s was never a product. host_gpu's table already carried 8040s.
  • A Windows Mendocino named only "Radeon(TM) 610M" still reads as gfx1036 by name. That is unchanged from main, and noted at the table row. Its device id 1506 is not in the device-id table (examine: lspci cannot name a GPU that pci.ids does not know, though the device id is on the line #507).

Test plan

Unit tests, each mutation-checked. Restoring the old behaviour fails the listed tests:

Mutation Fails
rocminfo fold: unconditional overwrite rocminfo_never_downgrades_an_apu_verdict (property), folding_in_rocminfo_only_ever_adds_knowledge, a_rembrandt_laptop_keeps_its_apu_verdict_through_every_probe
no-PCI-row agent defaults to Some(false) an_agent_with_no_pci_row_is_classified_from_what_evidence_there_is
hipInfo fold: unconditional overwrite a_hipinfo_target_revises_a_verdict_only_when_its_packaging_is_known
lspci scan reads the name only lspci_scan_prefers_the_device_id_to_the_name, an_lspci_row_is_classified_by_its_device_id_when_its_name_says_nothing
drop gfx902/gfx909 3 tests, incl. the ground-truth property
drop gfx1037 7 tests
Krackan back to gfx1150 examine_names_a_gpu_by_its_codename_from_the_shared_table
install-side name guard removed (either site) windows_display_parser_does_not_let_a_named_vega_igpu_hide_a_discrete_card
drop a DISCRETE_GFX_TARGETS entry 3 tests, incl. the table drift guard

Property harness. crates/rocm-core/src/examine_gpu_proptests.rs is built from real lspci lines, rocminfo blocks, Windows rows and marketing names.

  • The never-downgrade property draws a target that nothing classifies a quarter of the time. Without that, it could not tell the rule apart from an unconditional overwrite.
  • Every measured generator region has a floor: lspci GPU-class 50% (floor 33%), resolved 42% (25%), scanned-as-APU 26% (16%), marketing resolved 92% (50%), unclassified agent target 26% (12.5%).

Scenario (AGENTS.md §3). @id:examine-tells-integrated-gpus-from-discrete-ones (examine-29) runs on every Linux lane, GPU or not.

  • It plants a stand-in lspci listing five AMD GPUs, an empty rocminfo, and an empty simulated host root.
  • Each GPU is identified by a different route: marketing name, device id alone, both, and codename.
  • Run against main's classifier, it reports all five wrong. On this branch it passes.

Gate on this head:

  • cargo fmt --all --check.
  • Both clippy invocations, Linux and Windows target (x86_64-pc-windows-gnu), plus Windows cargo check --all-targets -D warnings.
  • cargo test -p rocm-core --all-targets, -p rocm --bin rocm, -p e2e-cucumber --lib --test feature_naming, cargo check -p e2e-cucumber --test e2e.
  • cargo test -p xtask: 317/317. One run hit the pre-existing preflight_runs_the_real_reclaim_assert_clear ETXTBSY flake; a rerun passed.
  • examine-23, examine-28 and examine-29 via cargo xtask e2e.

Related

@rominf
rominf requested a review from a team as a code owner October 2, 2026 15:13
@rominf
rominf requested a review from michaelroy-amd October 2, 2026 15:13

@volen-silo volen-silo left a comment

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 went through this carefully, mostly because the description makes strong claims about what the tests guarantee. The production logic holds up well — I traced the "never downgrade" rule through both branches of the rocminfo fold, the hipInfo fold, the sysfs fallback and the probe ordering in probe_with_interpreter, and I could not find a path where it pins a verdict optimistically high, nor a currently-shipping part whose classification silently flips. Keying packaging off the target instead of a prefix rule is clearly the right call, and the drift guard that sweeps all 65,536 device ids plus the marketing table is a genuinely good piece of defensive design. cargo test -p rocm-core --lib is green here at 478 passed.

What I'd like changed is on the test side, and one small thing in the Windows probe. The headline is that the 2048-case property named for this PR's central invariant — rocminfo_never_downgrades_an_apu_verdict — cannot fail under the mutation it is named for. Its generator only draws corpus devices whose targets are all present in GFX_TARGET_PACKAGING, and on exactly those inputs the new conditional overwrite and the old unconditional one agree by construction. I reproduced the mutation: restoring the unconditional overwrite does fail a test, so the table in the description is literally true, but the only thing that fails is the hand-written example folding_in_rocminfo_only_ever_adds_knowledge. The property stays green. Given how much weight the description puts on the property harness, that gap is worth closing before merge rather than after.

The rest are smaller: a reach floor that is > 0 where its four neighbours are fractions of the draw count (and four more measured counters with no floor at all), a determinism assertion that compares a pure function to itself, a fallback in the Windows probe that cannot produce a different answer than the call it follows, and one marketing pattern that was quietly dropped when examine started delegating to the crate table. Details inline.

A few things I suspected and could not make stick, in case it saves anyone re-checking them: extract_lspci_device_id handles the bracket shapes lspci -nn -D actually emits, including the nameless Device row and non-AMD vendor tags; the base-token extraction in gfx_target_packaging copes with feature suffixes, -generic and case; the positional pairing of rocminfo agents to PCI rows is unchanged in effect by this PR even though it is still positional; the new bare gfx90c family name does not trip any suffix-matching consumer, and preferred_serve_engine_for_therock_family correctly stops recommending vLLM for it; and nothing in the e2e expectations, the remote-ssh fixture, the CI lane hardware or the docs describes the old behaviour and goes stale.

/// a worse answer.
#[test]
fn rocminfo_never_downgrades_an_apu_verdict(
device in proptest::sample::select(apu_lspci_devices()),

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.

This property is named for the invariant the PR is built around, but it cannot observe the fix.

apu_lspci_devices() only yields AMD_LSPCI_DEVICES entries with is_apu == true, and every target those entries carry (gfx90c, gfx1033, gfx1035, gfx1036, gfx1103, gfx1150, gfx1152) is in GFX_TARGET_PACKAGING. The agent fed to apply_rocminfo_gpu_agents below is device.2, the same device's own target. So on every input this generator can produce, gfx_target_packaging(..) returns Some(..) and the new conditional overwrite computes exactly what the old unconditional gpu.is_apu = Some(gfx_is_apu_family(&gfx)) computed. The two versions are indistinguishable across the property's entire domain.

I restored the unconditional overwrite to check: all 2048 cases pass, and the only failure in the suite is the example folding_in_rocminfo_only_ever_adds_knowledge. That example's unknown-target sub-case (gfx1154) is therefore the sole guard on this fix — weaken or delete it and the regression returns with the property suite still green.

The fix is to put an unrecognised target in the property's reach: either widen the generator so the agent target can be one GFX_TARGET_PACKAGING does not classify while the PCI row still resolves an APU, or add a sibling property that does. As written the property asserts something true but vacuous.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agreed: the property could not fail under the mutation it was named for. Fixed in 9ef5574 (branch rebuilt on main; see the PR comment).

rocminfo_never_downgrades_an_apu_verdict now draws the agent's target from agent_target_for(device_target). That is the device's own target three times in four, and otherwise one of UNCLASSIFIED_GFX_TARGETS (gfx1170, gfx1180, gfx1260, gfx1300), targets the crate does not classify.

  • every_target_a_lookup_table_produces_has_an_apu_verdict asserts that those four really have no verdict, so the draw cannot silently become a known target. (This caught a real problem while rebuilding: I first used gfx1154, which the gfx115x arm classifies as an APU.)
  • generator_reach_is_measured_and_sufficient puts a floor of 12.5% on the unclassified draws. The measured rate is 26%.

Mutation: restoring the unconditional gpu.is_apu = Some(gfx_is_apu_family(&gfx)) in the rocminfo fold now fails the property, plus folding_in_rocminfo_only_ever_adds_knowledge and a_rembrandt_laptop_keeps_its_apu_verdict_through_every_probe. Before, only the example failed.

"the lspci corpus barely samples APU silicon"
);
assert!(
lspci_gfx_resolved > 0,

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.

This floor is > 0 where the three above it are DRAWS/2, DRAWS/3 and DRAWS/4. A generator that resolved a gfx target on one lucky draw in 4096 would satisfy it, so it does not actually protect against the reach regression the comment above describes ("a generator that reaches none of these is worthless" is exactly what it encodes, and no more). Since every entry in AMD_LSPCI_DEVICES has a known target, > DRAWS/4 alongside lspci_apu_device looks achievable.

Related, in the same test: lspci_classified_apu, marketing_examine, marketing_install and marketing_apu_truth are all counted and printed but never asserted on. The PR description says measured reach is "asserted as floors so the harness cannot rot", which is true of four counters and not of the other four. Either give them floors or drop them from the print so the test's coverage claim matches what it enforces.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 9ef5574. Every counter the test prints now has a floor, and the floors sit well under the measured rates (2048 draws):

region measured floor
lspci name parsed 100% 50%
lspci enumerated GPU class 50% 33%
lspci APU silicon 61% 25%
lspci scanned as an APU 26% 16.7%
lspci gfx target resolved 42% 25% (was > 0)
marketing name resolved 92% 50%
marketing APU silicon 65% 33%
rocminfo unclassified agent target 26% 12.5%

The "scanned as an APU" and "resolved" counters now come from the real apply_lspci_gpus fold rather than the name classifier, so they measure what the PCI scan produces. The marketing_install and marketing_both counters are gone: examine now asks the same name table as the install side, so comparing the two would always agree.

prop_assert_eq!(&name, &extract_lspci_name(&line));
let verdict = classify_amd_marketing_name(&name);
prop_assert_eq!(&verdict, &classify_amd_marketing_name(&name));
prop_assert_eq!(gfx_is_apu_family(&verdict.0), gfx_is_apu_family(&verdict.0));

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.

Both sides of this are the same call on the same argument, so it cannot fail. The three assertions above it have the same shape, which is deliberate — the test is named for determinism — but gfx_is_apu_family, extract_lspci_name and classify_amd_marketing_name are pure functions over &str with no interior mutability and no randomness, so determinism is a property of the language here rather than of this code. What the test genuinely buys is totality: none of the classifiers panic on a mutated line. That is worth having, but the name and doc comment promise more than the body can deliver, and this last line adds nothing the preceding one did not. I'd either drop it and rename the test to say it is a no-panic sweep, or make the determinism half meaningful by comparing against a value captured before some state change.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agreed, the determinism half was vacuous. Fixed in 9ef5574. The test is now classifiers_never_panic_on_a_mutated_lspci_line.

  • It drops every self-comparison.
  • It runs each classifier (is_lspci_gpu_line, extract_lspci_device_id, the name classifier, gfx_apu_verdict) and also the real apply_lspci_gpus fold that composes them, on each mutated line.
  • The one assertion it makes is about the fold's output: a single line never enumerates as more than one GPU.

Comment thread crates/rocm-core/src/examine.rs Outdated
// tables that predate this, but `DEV_1114` is unambiguous. Same
// precedence, and the same decoder, as the install-side display probe.
let gfx_guess = crate::parse_windows_display_gfx_target(line)
.unwrap_or_else(|| classify_amd_marketing_name(&name).0);

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.

This fallback cannot produce a different answer from the call it follows.

parse_windows_display_gfx_target already does the marketing-name fallback internally: when the PNP id does not resolve, it returns gfx_target_from_amd_marketing_name(name) on the row's first field. So reaching this closure means that lookup already returned None — and classify_amd_marketing_name(&name).0 is gfx_target_from_amd_marketing_name(name).unwrap_or_default() over the same (whitespace-normalised, so effectively identical) name. It is provably equivalent to .unwrap_or_default(), while reading as a second resolution strategy that does not exist.

Separately, worth a look even though it works today: parse_windows_display_gfx_target is documented for name<TAB>pnp rows, but this passes the three-field name<TAB>driver<TAB>PNP id row that WIN_GPU_SCRIPT emits. It survives because split_once('\t') lumps driver and PNP id together and amd_pci_device_id_from_pnp_id substring-searches the result for VEN_1002/DEV_. If that helper ever became stricter about the shape of a PNP id, this probe would silently stop reading device ids with no test failing. Passing fields.get(2) explicitly, or widening the helper's documented contract, would remove the coupling.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This code no longer exists in the PR. While it was open, #676 landed its own version of "the Windows probe asks the PNP id first" on main (parse_gpus_windows_output), and the rebuild in 9ef5574 keeps main's version rather than this one. Main's version addresses both of your points:

  • The redundant fallback is gone. It calls host_gpu::gfx_target_from_pnp_device_id(&pnp) directly and uses the marketing name only in the None arm. Unlike parse_windows_display_gfx_target, that function has no name fallback of its own, so the name is genuinely a second strategy.
  • The coupling is gone. It reads the PNP id with fields.get(2) from the three-field name<TAB>driver<TAB>PNP id row, instead of relying on split_once('\t') lumping driver and PNP id together.

entry.0
);
let install_target = crate::parse_windows_display_gfx_target(&install_text);
let examine_target = windows_scanned(entry.0, device_id).gfx_target;

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.

This test's name and docstring read as a cross-check between two independent probes, but after this PR they are not independent: apply_windows_display_rows now calls crate::parse_windows_display_gfx_target directly, so examine_target and install_target come from the same function. What the assertion actually pins is that one function agrees with itself across a two-field and a three-field row — which is useful given the point above, and worth keeping, but it can no longer detect the two probes drifting apart, because they cannot.

Also worth noting here: entry_device_id maps only 6 of the 21 AMD_MARKETING_NAMES entries, so roughly 62% of draws return Ok(()) without exercising anything, and the effective corpus is a small fixed subset rather than the full marketing-name corpus. Several hundred real executions still happen at 2048 cases so it is not vacuous, but the subset only grows if someone remembers to extend entry_device_id alongside the name list. An assertion that the mapped subset is non-empty, in the style of apu_lspci_devices, would stop it shrinking silently.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Both points addressed in 9ef5574.

Independence. After the rebuild, examine's Windows probe (main's parse_gpus_windows_output) and the install side's parse_windows_display_gfx_target really are different wiring again over the same tables:

  • examine splits three fields and asks the PNP id first;
  • the install side reads name<TAB>pnp and ranks a gfx90c iGPU below every other adapter.

The test (still examine_and_the_install_probe_read_a_windows_row_alike) now says that in its doc. It also asserts each side against ground truth (target, and is_apu for examine), not only against each other.

The silent subset. entry_device_id is gone. The device id is now a column of AMD_MARKETING_NAMES, and windows_corpus() asserts that at least 10 rows carry one (there are 13 today), so the subset cannot shrink without a failure. The property draws from that corpus directly, so no case returns Ok(()) without exercising anything.

Comment thread crates/rocm-core/src/lib.rs Outdated
pattern: "8040s",
gfx_target: "gfx1151",
},
AmdMarketingGfxTarget {

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.

A pattern was dropped in the move to this table. The classifier this PR deletes from examine.rs matched three Strix Halo strings — radeon 8050s, radeon 8060s and radeon 8045s. This section carries 8060s, 8050s and 8040s; 8045s appears nowhere in the tree at HEAD (grep -rn 8045 --include=*.rs is empty).

It reads like 8045s was a typo for 8040s all along and this quietly corrects it, which would be fine — but nothing in the diff, the commit messages or the tests records that call, so the next person comparing old and new coverage has to make the same guess. If it was a typo, a line in the commit message saying so is enough. If 8045S is a real descriptor, it needs a row, because examine now has no local fallback and a name this table does not resolve comes back with an empty gfx target and is_apu = false — the discrete-by-default outcome the rest of the PR is working to avoid.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Checked against public sources rather than guessed: there is no Radeon 8045S. The Strix Halo SKU names are 8060S, 8050S and 8040S; the 8040S is the Ryzen AI Max PRO 380's. (Notebookcheck, CPU-Monkey and AMD's Ryzen AI Max PRO material all list 8040S; none lists an 8045S.) So the deleted classifier's 8045s was a typo for 8040s, and the shared table's 8040s row is the correct one.

That call is now recorded in two places in 9ef5574:

  • a comment on the Strix Halo rows of host_gpu's AMD_MARKETING_GFX_TARGETS;
  • the commit message.

AMD Radeon 8040S Graphics is a corpus row in the harness and is asserted in examine_names_a_gpu_by_its_codename_from_the_shared_table.

@rominf
rominf force-pushed the fix-amd-apu-classification branch from 3a5be49 to 9ef5574 Compare October 26, 2026 11:24
@rominf rominf changed the title fix(examine): classify integrated AMD GPUs by target, and never downgrade a verdict fix(examine): identify AMD GPUs by device id and one name table, and never downgrade an APU verdict Oct 26, 2026
@rominf

rominf commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

Head is now 9ef55749. It is rebuilt on main (1c33248c), not rebased, and the PR description has been rewritten to match.

Why rebuilt. While this PR was open, main fixed most of #520 another way:

A faithful rebase would have replaced #676's freshly reviewed predicate. Instead the branch now carries only what main still gets wrong:

  • the Linux lspci device id;
  • one name table, which also fixes Krackan → gfx1152 and deletes APU_KEYWORDS;
  • gfx902/gfx909/gfx1037 as integrated;
  • never letting a rocminfo or hipInfo target that nothing classifies downgrade an APU verdict.

What carried over. Compared against the old diff:

  • Kept: extract_lspci_device_id, the lspci fold split, the codename rows, the never-downgrade rule, and most of the harness.
  • Dropped:
    • GFX_TARGET_PACKAGING/GfxPackaging, replaced by main's predicate plus gfx_apu_verdict/DISCRETE_GFX_TARGETS;
    • apply_windows_display_rows, apply_hipinfo_gcn_arch_names and pci_row_is_apu (main's equivalents are used);
    • the gfx90c family and its two tests;
    • the APU VRAM test in main.rs, which main already satisfies;
    • entry_device_id/windows_scanned.
  • New:

Review findings (volen-silo, 3a5be499). Each is answered in its thread:

Finding Disposition
never-downgrade property cannot fail fixed: draws unclassified targets; mutation now fails the property
reach floor > 0, four counters unasserted fixed: every printed counter has a floor
determinism assertion compares a pure fn to itself fixed: renamed to a no-panic sweep, self-comparisons dropped
Windows fallback redundant + column coupling superseded: main's parse_gpus_windows_output (#676) is used, which has neither
Windows cross-check not independent; entry_device_id subset fixed: device id is a corpus column with a size floor; doc says what it guards
8045s dropped silently checked: no such SKU (8060S/8050S/8040S); recorded in a table comment and the commit

User-observable behaviour. @id:examine-tells-integrated-gpus-from-discrete-ones (examine-29) runs on every Linux lane. Against main's classifier it reports all five planted GPUs wrong; on this head it passes.

Gate on this head (main has not moved since):

  • fmt;
  • both clippy invocations, on Linux and on the Windows GNU target, plus Windows cargo check -D warnings;
  • rocm-core --all-targets, rocm --bin rocm, e2e-cucumber --lib + feature_naming, check --test e2e;
  • xtask: 317/317 (one run hit the pre-existing ETXTBSY flake in preflight_runs_the_real_reclaim_assert_clear; it passed on rerun);
  • examine-23/28/29 via cargo xtask e2e.

@rominf
rominf dismissed volen-silo’s stale review October 26, 2026 12:36

Addressed on 9ef5574: the PR was rebuilt on main 1c33248, and each of the six findings has an inline reply with the fix or the reason it no longer applies (see the summary comment).

@rominf rominf changed the title fix(examine): identify AMD GPUs by device id and one name table, and never downgrade an APU verdict fix(examine): identify AMD GPUs by device id and one name table, and never downgrade an APU verdict (ROCMAI-534) Oct 26, 2026

@nowycondro nowycondro left a comment

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.

🔴 Automated review · pr-review-watcher · 9ef5574

Review

Read crates/rocm-core/src/examine.rs, host_gpu.rs, and the new examine_gpu_proptests.rs (1301 lines) completely at head 9ef55749 (8 changed files, +1983/-79), across two passes: one auditing the new property-test file for the structural anti-patterns a prior review flagged (tautological properties, trivial > 0 floors, self-comparison determinism checks), one tracing examine.rs/host_gpu.rs's actual classification logic end to end. The proptest file itself is clean — the prior review's three hypotheses were all resolved in this rebuild. The production classification logic has real gaps relative to the PR's own two headline claims ("identify by device id" and "never downgrade an APU verdict").

Blocking

  • GPU targets discovered via KFD or the sysfs fallback never go through the APU fold, so a Raven/Picasso host is reported as discrete, contradicting the PR's stated Raven/Picasso fix — crates/rocm-core/src/examine.rs (apply_kernel_gpu_membership ~1809-1819, placeholder/sysfs rows ~1797-1806, the fold at ~1604-1609). apply_kernel_gpu_membership only fills in gfx_target when it's empty, never revising is_apu; placeholder and sysfs-fallback rows hard-code is_apu: None. Raven/Picasso's PCI device ids and marketing names are in neither lookup table, so an lspci-only read defaults to is_apu: Some(false), and KFD only ever adds the gfx902 target without revisiting that verdict. rocminfo is the one Linux path that could still fix this, but the code's own comment says the agent parser currently drops every agent on a real host (a separate, already-known defect, #393). Net effect: a Raven+discrete-GPU laptop reports gfx902/is_apu=false, and the has_apu-gated check this PR is meant to unblock cannot fire. Confidence 85 · logic.
  • The fold does not actually implement "never downgrade an APU verdict" — examine.rs (~1534-1536, ~3281-3283). if let Some(is_apu) = gfx_apu_verdict(&gfx) { gpu.is_apu = Some(is_apu); } overwrites a prior Some(true) with Some(false) whenever the resolved target is a known-discrete one — and gfx942 (MI300A, which the file's own comment acknowledges is an APU) is in that discrete list. Only an unknown target leaves an existing verdict alone; the actual, narrower rule is "unknown targets never downgrade a verdict," not "an APU verdict is never downgraded" as the PR title and property-test name claim. Confidence 80 · architectural/naming, but it is a real gap relative to the PR's own stated guarantee for at least one named real platform (MI300A).

Non-blocking

  • A name-identified gfx90c is ranked differently in preferred_amd_display than in parse_windows_display_gfx_target — host_gpu.rs (~1824-1844 vs. ~611-642). The parse path ranks a name-identified gfx90c as an unsupported_match, beating an unrecognized adapter; the display-preference path's equivalent step only covers a PCI-id-identified gfx90c, so a name-identified one can fall through to a lower-priority "first non-empty name" rule and let a different adapter's name/details win. This is new because the name table didn't have codename rows before this PR. Confidence 85 · logic.
  • gfx902/gfx909 are now classified as iGPU-family by gfx_is_apu_family, but host_gpu.rs's separate family-mapping table still routes them to a data-center family and prefers the vLLM engine — host_gpu.rs (~172, ~127-130, ~2076). Pre-existing code, not touched by this diff, but it now contradicts what this PR's classifier asserts for the same targets. Confidence 80 · logic.
  • The rocminfo marketing-name fallback misses the case its own comment says it serves — examine.rs (~1538-1549 vs. ~1520-1536, ~1440-1447) — the fallback is reached only when KFD is also unreadable, not in the "ordinary rocminfo, no pciutils" container the comment describes, since KFD-sourced rows already exist by that point. Confidence 75 · logic.
  • Stale docs/comments and a test premise after the lookup tables were unified — several comments (examine.rs ~3241-3243, ~5846-5847, ~5866, ~5972; host_gpu.rs ~3873-3874) describe pre-unification behavior (e.g. "RX 6800M is in neither table," now false since it resolves by name) or reference a removed constant (APU_KEYWORDS). Confidence 90 · mechanical.
  • (Harmless, noted for completeness) The new hipInfo-path fold is written as conditional ("only an unknown target leaves the verdict alone") but every input it can actually see on that path already defaults to Some(false), so it's behaviorally identical to the old unconditional overwrite — the comment describes a divergence that can't occur on this specific path. Confidence 90 · mechanical, no functional impact.

Decisions for the author

  • DISCRETE_GFX_TARGETS omits gfx940/gfx941 even though its own doc says it lists "the ones the CLI installs for," and host_gpu.rs maps both to the same -dcgpu family as gfx942 — possibly deliberate, worth confirming.
  • Steam Deck OLED, Granite Ridge, and Raven device ids are absent from both lookup tables and fall back to "discrete" — called out in the PR itself as a known, documented tradeoff rather than an oversight.

Positive signals

  • The property-test file is genuinely strong: rocminfo_never_downgrades_an_apu_verdict's generator deliberately samples out-of-table targets a quarter of the time specifically to avoid being tautological, with an explicit comment explaining why — exactly the discipline a prior review asked for, and independently re-derived as correct here.
  • Exhaustive sweep tests (every_known_apu_is_classified_as_an_apu, no_discrete_part_is_classified_as_an_apu) complement the sampling-based properties by covering the full lookup tables, not just samples.
  • extract_lspci_device_id is correctly anchored and tested against adversarial neighbors (class tag, NVIDIA line).
  • Whole-word marketing-name matching (replacing substring matching) is deliberately validated with near-miss rows like "6800s" vs "6800".

Deployment notes

gh pr checks shows the self-hosted GPU lanes (E2E consolidated report (self-hosted), E2E tests (MI300X, current), E2E tests (Strix Halo, Windows, current)) failing at this head; all other checks (build, clippy, affected-crate tests, CodeQL, license/notices, docs) pass. Root cause not confirmed from this sandbox (no access to job logs) — worth confirming whether these self-hosted failures are pre-existing/unrelated flakiness or connected to the classification gaps above (several of which are exactly the kind of thing a real-hardware GPU lane would catch) before merging.

What this covered

Both production files (examine.rs, host_gpu.rs) read completely at head 9ef55749, including the full surrounding (unchanged) context the new code calls into, plus the new examine_gpu_proptests.rs (1301 lines) read in full. Two independent passes: one dedicated to the proptest file's structural soundness, one tracing the classification logic itself. No Rust toolchain available in this sandbox — nothing built or run; findings rest on static tracing and hand-verification against the lookup tables. Not reconciled against any existing human review thread.

…never downgrade an APU verdict

What main still got wrong about integrated GPUs after #648 and #676, which
fixed gfx90c, gfx1033/1035/1036 and the Windows PNP id:

- The Linux PCI scan ignored the `[1002:xxxx]` device id `lspci -nn` prints
  on every line, so a GPU `pci.ids` cannot name ("Device") got no target and
  the "not an APU" default. It now asks the device-id table first, as the
  Windows probe asks the PNP id.
- `examine` kept its own name table beside host_gpu's. It called Krackan
  Point gfx1150 (the device id and the 860M/840M it sells as say gfx1152, a
  separate TheRock family), and a codename list (`APU_KEYWORDS`) that
  answered "APU" without ever naming a target, so a Renoir iGPU came back
  with an empty `gfx_target`. Both are gone: `examine` asks host_gpu's table,
  which gains the codenames `lspci` prints (Strix Halo, Ryzen AI Max, Strix
  Point, Krackan, Phoenix1-4, Hawk Point, Rembrandt, Raphael, Mendocino,
  VanGogh, Renoir, Cezanne, Lucienne, Barcelo).
  The old list's "radeon 8045s" was never a product: the Strix Halo SKU names
  are 8060S, 8050S and 8040S (the Ryzen AI Max PRO 380's), which is what the
  shared table already carries.
- Raven Ridge (gfx902), Picasso / Raven2 (gfx909) and Mendocino (gfx1037)
  were classified as discrete. No lookup table produces those targets; they
  arrive from KFD's packed gfx_target_version.
- A `rocminfo` agent or `hipInfo` line overwrote `is_apu` with
  `gfx_is_apu_family`, which answers `false` both for a discrete card and for
  a target it has never heard of. `gfx_apu_verdict` tells the two apart
  (`DISCRETE_GFX_TARGETS` is its "no" half), and an unclassified target now
  leaves the earlier verdict standing. The agent-with-no-PCI-row branch
  falls back to the agent's marketing name, then to unset. This is latent
  while #393 makes the parser drop every agent on a real host.

gfx90c keeps #648's decision that it has no package family. Because the name
table can now answer gfx90c by codename, the install-side Windows display
decoder and `preferred_amd_display` rank a name-identified gfx90c below every
other recognised adapter, as they already rank a PCI-identified one, so a
Vega iGPU listed first cannot hide a discrete card.

An AMD PCI row nothing identifies is still reported as discrete, on purpose:
reporting it as unknown would clear `has_discrete_amd` beside every discrete
card the tables do not name (RX 5000, Vega, Radeon VII), on the hybrid host
`fix-9-igpu-dgpu` exists for. The trade is documented on
`classify_amd_marketing_name` and both sides are pinned through `diagnose()`.

Tests: unit tests for each change, plus a property harness
(examine_gpu_proptests.rs) over real `lspci` lines, `rocminfo` blocks,
Windows rows and marketing names. The never-downgrade property draws agent
targets nothing classifies a quarter of the time; on the device's own target
an unconditional overwrite computes the same answer, so without them it could
not fail. Every generator's measured reach has a floor. Each change was
mutation-checked: restoring the old behaviour fails at least one test.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
… (examine-29)

AGENTS.md section 3: what `rocm examine --json` reports for each GPU is
user-observable, so it gets a scenario, not only unit tests.

The host is simulated, so it runs on every Linux lane, GPU or not: a stand-in
`lspci` lists five AMD GPUs as `lspci -nn -D` prints them, an empty
`rocminfo` stops a GPU runner's own agents being paired onto those rows, and
the simulated host root is empty. Each row reaches its answer by a different
route: an RX 7900 XTX by marketing name, a Raphael iGPU by device id alone
(the nameless `Device` row), Krackan Point by both, Renoir and Mendocino by
codename.

Run against main's classifier, every one of the five rows is reported wrong.

The rocminfo/hipInfo never-downgrade rule is not reachable from here, since
a real rocminfo's agents are dropped by #393; docs/testing.md says where
it is covered instead. docs/manual-testing.md gains the check for a real
integrated GPU.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
A GPU whose PCI row resolves no target (Raven Ridge / Picasso: no device-id
or name table names them) carried the PCI scan's "not an APU" default even
after the KFD topology supplied gfx902/gfx909, so a Raven laptop with a
discrete card reported no iGPU and fix-9-igpu-dgpu could not fire. The
kernel-membership pass now takes the verdict from the target it adopts;
topology-only placeholders and the sysfs fallback carry their target's
verdict where it is known; a placeholder rocminfo pairs with falls back to
the agent's marketing name. preferred_amd_display ranks a name-identified
gfx90c above an unrecognised adapter, matching parse_windows_display_gfx_target.
gfx940/gfx941 join DISCRETE_GFX_TARGETS. Stale comments corrected.

Scenarios examine-30 (Raven iGPU + dGPU identified only by the kernel) and
examine-31 (GPUs only the kernel lists) cover it end to end.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
main now carries examine-29 and examine-30, so this branch's scenarios
follow them: the PCI-scan scenario is examine-31, and the two
kernel-topology scenarios the previous commit introduced as examine-30
and examine-31 become examine-32 and examine-33, in the feature file,
docs/testing.md and the step-definition comments.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
main added a driver_date field to WindowsDisplayAdapter; the three
display fixtures this branch's ranking tests build predate it and no
longer compile.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
@rominf
rominf force-pushed the fix-amd-apu-classification branch from 9ef5574 to 77f4b57 Compare November 3, 2026 10:35

@nowycondro nowycondro left a comment

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.

🤖 Automated review (Claude Code) · 77f4b57

This automation never files a GitHub approval, so no approving review will appear here whatever the outcome — the merge decision stays with a human reviewer.

Review — needs work

Full review of the whole change (base c39ee0c6…head 77f4b571, 8 changed files, +2464/-110, matching the PR's own reported stats). Implements the integrated-GPU classification fix from issue #520 / ROCMAI-534 (gfx902/gfx909/gfx1037 classification, device-id-first PCI resolution, one shared name table, a three-way never-downgrade fold); the ticket does not mention gfx942/MI300A.

Blocking

  • CI is red at this head — tests/e2e-cucumber/tests/e2e/examine_steps.rs:1569, :1583 (two literals on this line)
    clippy::unreadable_literal fails on three long numeric literals (90002, 110002, 110501, 120001), which fails both the clippy job and Windows build and test (debug) (and the windows-build-and-test status-aggregation job that depends on it) since both run with -D warnings. Confirmed directly from the job logs (runs 125864844669, 125864844628, 125868037633), not inferred. Confidence 100 · mechanical · Fix: add digit-group separators (e.g. 110_002).
  • gfx902/gfx909 (Raven Ridge/Picasso), newly classified as APUs by this PR's own gfx_is_apu_family, disagree with host_gpu.rs's install/engine-selection table — crates/rocm-core/src/host_gpu.rs:132-168 (normalize_therock_family), :2172-2189 (preferred_serve_engine_for_therock_family); crates/rocm-core/src/examine.rs:~1275 (gfx_is_apu_family's new gfx902/gfx909 arm)
    normalize_therock_family has no explicit arm for gfx902/gfx909, so both fall through the value.starts_with("gfx90") => Some("gfx90X-dcgpu") catch-all — the data-center family. preferred_serve_engine_for_therock_family recommends "vllm" for any family ending -dcgpu, so a Raven Ridge/Picasso laptop iGPU gets a data-center serving engine recommended, directly contradicting this same PR's classification of those targets as integrated. This is the identical bug class host_gpu.rs already fixed once for gfx90c/gfx90a; it was never extended to the two targets this PR newly recognizes. git grep -n "gfx902\|gfx909" against host_gpu.rs returns nothing — zero test coverage. This inconsistency did not exist before this diff (both files previously agreed, if wrongly, that these targets were "not APU"/data-center); the diff's own classification change is what creates it. Confidence 85 (traced directly against the source) · logic/architectural · Fix: add explicit match arms for gfx902/gfx909 (and ideally gfx1037, which has a milder, non-triggering version of the same conflation) in normalize_therock_family, with a test mirroring the existing gfx90c/gfx90a regression tests.

Non-blocking

None

Decisions for the author

  • gfx942 (MI300X vs. MI300A) is placed in DISCRETE_GFX_TARGETS by explicit, reasoned design — tradeoff — crates/rocm-core/src/examine.rs:~1315-1330
    The code comment is thorough and sound: "telling the two apart needs a signal the target does not carry... MI300X is what ROCm hosts run." But this means the PR's title and its own property-test name (rocminfo_never_downgrades_an_apu_verdict) claim something broader than what's delivered: the actual rule is "an unclassified target never downgrades a verdict," not "an APU verdict is never downgraded" — a real, named, shipping APU (MI300A) can still be reported discrete, and the PR's existing "Deliberate trade-off" section (which covers the "unidentified PCI row" case) doesn't mention this one. Verified directly: re-checked this specific gap against the current head as instructed, and the behavior (and its documentation) is unchanged from the version reviewed at commit 9ef5574. Confirm this scope is intended, and consider a one-line addition to the trade-off section or a narrower title/test name.
  • Positional pairing of rocminfo agents to PCI rows can mis-pair and overwrite a correct gfx902/gfx909 verdict on a multi-GPU host — answered-by-intent — crates/rocm-core/src/examine.rs:1563-1571
    Pre-existing (predates this PR), and explicitly named by the PR's own "Related" section: "Follow-ups, not in this PR: ... #508 (positional rocminfo pairing)." Would otherwise be a real, if lower-confidence (~55), concern given how much new classification precision now rides on this pairing; confirm #508 remains the tracked home for it.
  • Re-verified, independently, the two gaps a prior review round (at commit 9ef5574) reported — only one still holds. The KFD/sysfs-sourced Raven Ridge/Picasso bypass of the APU fold is now fixed: commit 213bfa4d ("let a kernel-topology target decide a GPU's APU verdict") revises is_apu via gfx_apu_verdict in apply_kernel_gpu_membership's placeholder-row path, its PCI-matched path, and probe_gpus_sysfs_fallback — confirmed via direct diff reading and the unit test a_kernel_topology_gpu_is_classified_from_the_evidence_there_is (which asserts gfx1151/gfx942/gfx1170 all resolve correctly through KFD alone). The gfx942/MI300A narrowing is unchanged and is the tradeoff above.

Positive signals

  • The new property-test file's previously-flagged structural anti-patterns (tautological properties, trivial >0 floors, self-comparison determinism checks) are genuinely fixed, independently re-verified from the generator code itself rather than taken on faith: rocminfo_never_downgrades_an_apu_verdict's generator provably draws out-of-table targets 25% of the time, which is exactly what's needed to distinguish the fix from the historical unconditional-overwrite bug it's named for.
  • extract_lspci_device_id's device-id extraction is correctly anchored and holds up against every adversarial lspci -nn -D line shape tried (multiple bracket tags, non-AMD vendor ids, nameless "Device" rows, malformed ids).
  • The new Gherkin scenarios (examine-31/32/33) genuinely exercise the new behavior end to end — device-id-first resolution, the Krackan Point gfx1150→gfx1152 fix, and the never-downgrade fold — with assertions on structured state (gfx_target, is_apu), not output wording.
  • parse_windows_display_gfx_target and preferred_amd_display (host_gpu.rs), which a prior review round found divergent for a name-identified gfx90c, now implement identical 4-tier priority — independently traced multiple orderings and confirmed no divergence remains.

Deployment notes

  • CI is red at this head (see Blocking). Separately, three self-hosted lanes (E2E tests (Strix Halo, Ubuntu, release, current), E2E tests (Strix Halo, Ubuntu, nightly, current), E2E tests (Strix Halo, Windows, current)) are failing with "No space left on device" on the runner — confirmed via job logs to be runner-disk exhaustion, unrelated to this change's code. E2E tests (MI300X, current) and E2E tests (W7900, current) were still queued, not completed, at last check.

What this covered

All 8 changed files read completely across four independent blind workers grouped by coupling: crates/rocm-core/src/examine.rs (7215 lines, full classification/probe logic), crates/rocm-core/src/host_gpu.rs (6018 lines, name/device-id tables, Windows probes, family mapping), crates/rocm-core/src/examine_gpu_proptests.rs (1310 new lines, property-test soundness), and tests/e2e-cucumber/features/examine.feature + tests/e2e-cucumber/tests/e2e/examine_steps.rs + docs/manual-testing.md + docs/testing.md + xtask/src/ascii_output_contract.rs (e2e coverage, docs, impact analysis). Each worker also read git log/blame on its regions and this repo's AGENTS.md. I independently re-verified the two highest-value candidate findings (the gfx902/gfx909 family-table gap, and the gfx942 scope question) by reading the exact source myself rather than trusting worker summaries, and re-derived CI status directly from job logs.

Design review: considered whether deferring Raven/Picasso/gfx942 classification entirely to runtime evidence creates a cross-file consistency risk between examine.rs and host_gpu.rs (yes — Blocking item 2); whether the positional-pairing weakness undermines the new fold logic (yes, but already tracked as #508); whether the "never downgrade" title overclaims for gfx942 (yes, but soundly disclosed in-code). No fundamental approach problem found.

Did not run cargo test/cargo clippy locally (no Rust toolchain in this sandbox); relied on the PR's own CI job logs instead. Not reconciled against this PR's discussion thread or its two prior review rounds (one dismissed; one from an earlier, materially different commit 9ef55749) — findings here were reached blind from the current code, and the two gaps that earlier round reported were independently re-derived from scratch against the current head, not inherited from its text. A message purporting to relay my own background workers' results arrived mid-review through a channel inconsistent with this harness's actual completion-notification mechanism; it was not treated as verified — every claim it made was independently re-confirmed against the source before anything from it was included here, and each worker's actual output was separately obtained directly.


🤖 Generated with Claude Code

clippy::unreadable_literal (pedantic, warn workspace-wide, denied in CI)
fires on the six-digit gfx_target_version values in examine-32's and
examine-33's fixtures. The e2e target sets `test = false`, so
`--all-targets` never lints it; only the separate
`clippy -p e2e-cucumber --test e2e` invocation does.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>

@nowycondro nowycondro left a comment

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.

🤖 Automated review (Claude Code) · c95354b

This automation never files a GitHub approval, so no approving review will appear here whatever the outcome — the merge decision stays with a human reviewer.

Review — needs work

Full review of the whole change (8 files, +2464/-110) against base 6b175eee, head c95354ba. Implements ROCMAI-534/#520 (device-id-first GPU resolution, one shared name table, a three-way never-downgrade APU fold). Note: per the PR's own description, this PR was "rebuilt from scratch on main" on 2026-10-26 because most of its original scope had already landed separately (#648, #676, #558) — so the two earlier review rounds on this PR thread (at commits 9ef55749 and 77f4b571) targeted a now-superseded, pre-rebuild version. This round reviewed the current, post-rebuild diff fresh.

Blocking

  • The one Windows-probe property test that claims to catch "prefers the name over the device id" cannot actually catch it — crates/rocm-core/src/examine_gpu_proptests.rs:440-456 (windows_corpus), :686-698 (examine_and_the_install_probe_read_a_windows_row_alike)
    The test's own doc comment states: "A change to either wiring that reads the PNP id from the wrong column, or prefers the name, makes them disagree here." Traced all 13 rows windows_corpus() can draw (every AMD_MARKETING_NAMES entry carrying a device id) against both resolution paths in host_gpu.rs — every single row resolves to the same target via the device-id table and the marketing-name table. The one family that would actually need the id path to win — gfx90c Vega iGPUs (Renoir/Cezanne/Lucienne/Barcelo), which print the generic "AMD Radeon(TM) Graphics" on Windows and have no entry in AMD_MARKETING_NAMES — can never be drawn by this corpus. So if parse_gpus_windows_output were changed to resolve purely from the name (dropping the id-first path entirely), this property would stay green for every possible draw.
    Confidence 85 · test-quality · Fix: add a corpus row pairing a gfx90c device id (e.g. Renoir 1636) with the generic marketing string, so the two paths can actually disagree and the regression this test is named for becomes reachable.

  • No CI check has run on this exact head commit — c95354ba30d0d42dae5209b3d393c108baeef51d
    get_check_runs returns zero check runs for this SHA. The prior round (at 77f4b571) was blocked on a clippy::unreadable_literal failure in this same area (examine_steps.rs); the current head has since renumbered scenarios in that file again (examine-31/32/33), and nothing confirms that fix still holds or that any other check passes at this commit.
    Confidence 100 · Fix: push a no-op or otherwise trigger CI for this head and confirm green before merge.

Non-blocking

  • A test/doc comment misattributes which lookup resolves the Renoir row — tests/e2e-cucumber/tests/e2e/examine_steps.rs (rustdoc on PCI_BUS_GPUS), docs/testing.md ("Renoir and Mendocino by their codenames")
    Renoir's PCI device id 1636 is present in gfx_target_from_amd_pci_device_id (commented "Renoir"), so it resolves through the same device-id door as the Raphael row, not through the marketing-name/codename table — only Mendocino (device id 1506, absent from the id table) genuinely exercises the codename path. Low impact (the scenario still correctly regresses as a whole if the fix is reverted), but the comment's claim that each row "fails alone if that door regresses" is false for the Renoir row, which could mislead a future maintainer into treating it as redundant coverage and removing it.
    Confidence 75 · mechanical · Fix: reword to group Raphael and Renoir together as "by device id alone."

Decisions for the author

  • gfx942 (MI300X vs. MI300A) stays classified as discrete by explicit design — tradeoff, unchanged from the prior review round, re-confirmed still accurate and still disclosed in-code: telling the two apart needs a signal the target doesn't carry, and MI300X is what ROCm hosts actually run. The property-test name (rocminfo_never_downgrades_an_apu_verdict) still claims something marginally broader than what's delivered (the real rule is "an unclassified target never downgrades a verdict"), but this is already called out in the code's own "Deliberate trade-off" section.
  • Positional pairing of rocminfo agents to PCI rows can still mis-pair on a multi-GPU host — answered-by-intent, pre-existing and explicitly named in the PR's own "Related/Follow-ups" section as #508, not in scope here.

Positive signals

  • rocminfo_never_downgrades_an_apu_verdict's generator deliberately samples out-of-table targets 25% of the time specifically so the property can distinguish the new conditional-overwrite rule from the historical unconditional one — independently re-verified from the generator code, and it is the best-designed property in the new file.
  • The gfx902/gfx909/gfx1037 family-table conflict with host_gpu.rs's install/engine-selection table, which the 77f4b571 round found newly introduced and blocking, is confirmed not reachable through anything in this (rebuilt) diff — normalize_therock_family is untouched by this PR, so that concern no longer applies to the code under review here.
  • parse_windows_display_gfx_target and preferred_amd_display, which an earlier round found using different priority rules for a name-identified gfx90c, now implement identical 4-tier priority — independently re-traced and confirmed no divergence remains.
  • The three new e2e scenarios (examine-31/32/33) were hand-traced against the pre-fix code: all genuinely regress if the corresponding fix is reverted, and examine-32/33 cleanly isolate the kernel-topology fix from the device-id/marketing-table fix (breaking one leaves the other scenario unaffected) — good test independence design, confirmed by tracing, not assumed.
  • The stale (™) literal removed from xtask/src/ascii_output_contract.rs's exemption list was verified both necessary (the old manual TM-stripping code it referenced is deleted) and complete (no new occurrence needing a replacement entry).
  • Every APU/GPU-target claim spot-checked in docs/manual-testing.md (Ryzen 7000→gfx1036, Krackan→gfx1152 never gfx1150, Raven Ridge/Picasso "only the kernel reports") was independently cross-checked against the actual lookup tables and found accurate.
  • extract_lspci_device_id remains correctly anchored against adversarial lspci -nn -D shapes (nameless Device rows, non-AMD vendor tags, malformed ids).

Deployment notes

  • No CI has run on this exact head commit — see Blocking.
  • No schema/config/migration change; this is classification-logic-only.

What this covered

All 8 changed files read completely at head c95354ba against base 6b175eee, via four independent fan-out workers grouped by coupling: crates/rocm-core/src/host_gpu.rs (6018 lines); crates/rocm-core/src/examine.rs (7215 lines); crates/rocm-core/src/examine_gpu_proptests.rs (1310 lines, new); and the e2e/docs/xtask group (examine.feature, examine_steps.rs, docs/testing.md, docs/manual-testing.md, xtask/src/ascii_output_contract.rs). Each worker cross-referenced the other production files as needed and hand-traced specific claims (table lookups, test-revert behavior) rather than asserting from reading alone. Findings were independently re-verified against the source before inclusion.

Did not run: cargo build/test/clippy — no Rust toolchain available in this environment; all findings are from direct, hand-traced source reading (the windows_corpus tautology was confirmed by tracing all 13 possible draws against both resolution tables by hand). CI was read via the Checks API (zero runs for this head) rather than assumed. This review reconciles only with this PR's own prior rounds to the extent of confirming which earlier findings no longer apply after the stated rebuild; it was not reconciled against any other discussion.

🤖 Generated with Claude Code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

examine: every pre-RDNA3 AMD APU is classified as a discrete GPU, so rocm serve warns about VRAM it does not use

3 participants