Skip to content

Treat GPU activity as optional and hand the worktree back clean - #2

Closed
zhiding512 wants to merge 2 commits into
jhinpan:skill/validate-kernel-prfrom
ROCm:zhiding512/validate-kernel-pr-amdsmi-fix
Closed

Treat GPU activity as optional and hand the worktree back clean#2
zhiding512 wants to merge 2 commits into
jhinpan:skill/validate-kernel-prfrom
ROCm:zhiding512/validate-kernel-pr-amdsmi-fix

Conversation

@zhiding512

@zhiding512 zhiding512 commented Aug 31, 2026

Copy link
Copy Markdown

Addresses items 1-3 of @zufayu's review at head 706eafafc, plus one related portability gap found while reproducing item 1. Based directly on 706eafafc, so it applies to the current PR head.

What changed

1. amdsmi_get_gpu_activity is no longer a hard dependency (review item 1)

A new read_activity() helper returns None for an unavailable metric instead of raising, and is shared by the picker and both inline probes in validate_pr.sh (record_gpu_activity_after, and the gpu_claim metadata probe). gpu_claim.idleness_basis now reports the evidence the claim rests on, in the shape you already use for arch_coverage_basis:

  • activity+vram — busy percentages were measured
  • vram-only — the activity API was unavailable; only resident VRAM separated the devices

Unknown stays distinct from zero. The previous gfx = gfx if isinstance(gfx, int) else 0 already silently reported an N/A metric as a measured-idle GPU, which is the overclaim-by-omission this PR argues against, so that substitution is gone too. Selection still prefers GPUs whose idleness was actually measured, and a GPU with a known-busy reading is still excluded.

The gpu_claim skip no longer conflates two different facts: "GPUs present but none idle" is an environment fact, while "AMD SMI is unqueryable" is a portability gap in the validator and says nothing about the GPUs.

2. The worktree is handed back in the state it was supplied (review item 2)

merge_sim's patch application now carries its own PATCH_APPLIED flag, reverted in cleanup(). BASE_ACTIVE keeps its original meaning of "currently in base state".

One correction to the review's diagnosis: the residue was not limited to the paths where BASE_ACTIVE stays 0. cleanup() called restore_head, which re-applies the patch, so even a run that reached the baseline path exited with the worktree patched. Every path leaked, and the NO_GPU branch was just the easiest one to hit.

3. pick-idle-gpu.py has a shebang (review item 3)

4. The amdsmi import fallback did not cover ROCm >= 7.1 (not in the review)

On ROCm 7.2 the Python bindings ship at $ROCM_PATH/share/amd_smi; /opt/rocm/libexec/amdsmi_cli is the CLI package and exposes no amdsmi module. The fallback probed only the latter, so on those containers the picker exited 2 with No module named 'amdsmi' before reaching any activity query — the same NO_GPU end state as item 1, from an unrelated cause. Item 1's fix does not help here, since this fails at import. The search list now also probes $ROCM_PATH, /opt/rocm and /opt/rocm-* under share/amd_smi, newest first.

SKILL.md documents the optional metric, idleness_basis, the split skip semantics, and the worktree revert. report_schema.json declares idleness_basis rather than letting it appear undeclared.

Verification

Host: MI308X (gfx942) x8, ROCm 7.2.0, amd-smi 26.2.1, torch 2.9.1, amdgpu 6.16.13.

Caveat on the evidence, stated up front: error 43 does not reproduce natively on this host. amdsmi_get_gpu_activity works here and returns gfx_activity: 0 on all 8 GPUs, and a wider probe (gpu_metrics_info, power_info, board_info, process_list) found no failure either. Error 43 comes from native libamd_smi.so, so it is amd-smi/ROCm-version dependent, not a gfx942 hardware trait — the driver is shared between host and container here, and only the userspace library version differs. Item 1 was therefore verified by fault injection: a shim wrapping the real amdsmi that forces amdsmi_get_gpu_activity to raise AmdSmiLibraryException(43). Items 2-4 are verified natively. I have no direct observation of the ROCm 7.0 native failure and am not claiming one.

Reproduced first, against unmodified 706eafafc:

Call site Before
pick-idle-gpu.py:67 exit 2, AMD SMI probe failed: 43 | AMDSMI_STATUS_UNEXPECTED_DATA
validate_pr.sh:462 metadata probe GPU_INFO_RC=1, empty output, gpu_claim: skip
validate_pr.sh:314 post-run tolerated, but prints a full traceback
picker, no fault, container as-is exit 2, No module named 'amdsmi'
worktree after one NO_GPU run M utils.py + ?? test_flydsl_utils.py; second run: 8 stage result was missing notes + not isolated-clean

I also confirmed empirically that PICKER cannot route around item 1: a working VRAM-only picker returned exit 0 and a valid HIP index, and gpu_claim still skipped, because the probe at :437 loads $SCRIPT_DIR/pick-idle-gpu.py rather than $PICKER. After this change the inline calls tolerate the failure, so there is nothing left to route around and I did not alter that $SCRIPT_DIR reference.

After the change:

Scenario Result
Fault injected, all three call sites picker exit 0 vram-only; probe RC=0 with gfx_activity_before_pct: null; post-run records a note, no traceback
Container as-is, no fault exit 0, activity+vram, all 8 GPUs idle
HIP 0-3 reporting 90% busy correctly excluded, picks HIP 4, basis activity+vram
Partial failure: 0-1 fail, 2 busy, rest idle picks HIP 3 — measured-idle preferred over unknown, busy still excluded
All 8 reporting 90% busy exit 1, not a vram-only fallback
All 8 failing exit 0, basis honestly vram-only
NO_GPU path, two consecutive runs worktree clean both times, 0 missing-stage notes
Real-GPU path, two consecutive runs worktree clean both times
SIGTERM / SIGINT while patch applied worktree clean (unpatched: residue)
SIGTERM in the base-state window worktree clean (unpatched: residue)

A full real-GPU run reaches the baseline block, so the BASE_ACTIVE path is genuinely exercised rather than skipped:

gpu_claim: pass, arch gfx942, idleness_basis "activity+vram",
           gfx_activity_before_pct 0, gfx_activity_after_pct 0
baseline_control: pass, repo_tests.state "target-not-present"
correctness_repo_tests: pass, tests:1 executed:1
arch_coverage: {"gfx942": "runtime"}

The interrupt and residue tests were checked against unmodified 706eafafc first to confirm they discriminate, so "clean" is a real result rather than a test that cannot fail.

Test plan

  • tests/test_validator.py: 24 passed — same before and after, no regression
  • ruff check pick-idle-gpu.py clean; bash -n validate_pr.sh clean; report_schema.json parses
  • Patch applies cleanly to 706eafafc and the applied files are byte-identical to the tested ones
  • Native confirmation on a ROCm 7.0 host that error 43 is gone — I could not reproduce it natively and would appreciate @zufayu re-running on the MI308X box that surfaced it

Not included

Review item 4 — making the validation report expected rather than required for FlyDSL kernel PRs in review-pr — is a judgement about your own skill's mandate rather than a defect, so I left that wording to you.

Made with Cursor

Summary by Sourcery

Make kernel validation portable across AMD SMI environments, preserve worktree cleanliness, and extend evidence collection for script and shape-driven targets.

New Features:

  • Support shape-grid validation through a target’s command-line shape argument and profile script targets with validator-owned execution receipts.

Bug Fixes:

  • Treat unavailable AMD SMI activity metrics as unknown rather than measured idle, while preserving VRAM-only GPU selection and honest report metadata.
  • Restore the supplied worktree after validation, including degraded paths and interrupts, so repeated runs do not inherit patch residue.
  • Support AMD SMI Python bindings across newer ROCm installation layouts.

Enhancements:

  • Distinguish GPU-environment skips from AMD SMI portability failures and record whether idleness was established with activity and VRAM or VRAM alone.
  • Validate execution receipts even when shape-grid coverage is unavailable, without overstating shape coverage.

Documentation:

  • Document optional GPU activity metrics, idleness evidence, skip semantics, worktree restoration, script profiling, and CLI-based shape grids.

Chores:

  • Add a shebang to the idle-GPU picker.

The head branch lives in ROCm/aiter as zhiding512/validate-kernel-pr-amdsmi-fix rather than in a personal fork, so you may not have write access to push to it. If you want changes made to this branch, say so and I will push them.

amdsmi_get_gpu_activity was a hard dependency at three call sites, so a host
where that single query fails degrades the whole run to NO_GPU even with idle
GPUs available. @zufayu hit this on MI308X / ROCm 7.0, where the call returns
AMDSMI_STATUS_UNEXPECTED_DATA (43) while enumeration, BDF, ASIC and VRAM queries
all work. PICKER could not route around it because the executor re-queries the
API inline rather than going through the picker's selection.

Activity is now optional. A new read_activity() helper is shared by the picker
and both inline probes, and gpu_claim.idleness_basis names the evidence the
claim rests on: activity+vram, or vram-only when only resident VRAM separated
the devices. Unknown stays distinct from zero -- the previous
`gfx if isinstance(gfx, int) else 0` substitution reported an unavailable metric
as a measured idle GPU. The gpu_claim skip also stops conflating "GPUs present
but none idle", an environment fact, with "AMD SMI is unqueryable", a
portability gap in the validator.

The import fallback did not probe $ROCM_PATH/share/amd_smi, where the bindings
ship on ROCm >= 7.1, so on those containers the picker exited 2 before reaching
any activity query -- the same NO_GPU symptom from an unrelated cause.

Separately, merge_sim applied the patch but cleanup was guarded by BASE_ACTIVE,
which is set only inside the baseline path. A run that skipped correctness
exited with the patch applied, and when the baseline path did run, cleanup's
restore_head re-applied it, so every path left the caller's worktree patched and
the next run reported not isolated-clean against the caller. The application now
carries its own flag and is reverted on exit, including on interrupt.

Reported by @zufayu on #4870.
@sourcery-ai

sourcery-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR makes AMD GPU activity best-effort with honest VRAM-only reporting and selection behavior, expands AMD SMI import portability, and ensures validation always returns the worktree to its supplied state; documentation, schema, and fault/interrupt regression coverage are updated accordingly.

Sequence diagram for optional GPU activity selection

sequenceDiagram
    participant Picker as pick-idle-gpu.py
    participant AMD_SMI as AMD_SMI
    participant GPU as GPU
    Picker->>AMD_SMI: amdsmi_get_processor_handles()
    loop sampling window
        Picker->>AMD_SMI: read_activity(amdsmi, handle)
        AMD_SMI-->>Picker: GFX/UMC percentages or unavailable
        Picker->>AMD_SMI: amdsmi_get_gpu_vram_usage(handle)
        AMD_SMI-->>Picker: resident VRAM
    end
    alt activity measured
        Picker->>Picker: Select measured-idle GPU
        Picker-->>Picker: idleness-basis: activity+vram
    else activity unavailable
        Picker->>Picker: Select VRAM-eligible GPU
        Picker-->>Picker: idleness-basis: vram-only
    else known-busy GPU
        Picker-->>GPU: Exclude GPU
    end
Loading

Flow diagram for validator worktree restoration

flowchart TD
    Start([Validator starts]) --> Apply{Patch applies?}
    Apply -->|No| Exit([Exit with supplied worktree])
    Apply -->|Yes| Applied[Set PATCH_APPLIED=1]
    Applied --> Run[Run validation and optional baseline]
    Run --> Interrupt{Normal exit or interrupt}
    Interrupt -->|Either| Cleanup["cleanup()"]
    Cleanup --> Baseline{BASE_ACTIVE=1?}
    Baseline -->|Yes| ClearBase[Clear PATCH_APPLIED; remain in supplied state]
    Baseline -->|No| Revert[git apply -R patch]
    ClearBase --> Clean([Return worktree clean])
    Revert --> Clean
Loading

File-Level Changes

Change Details Files
Make GPU activity querying optional while preserving the distinction between unknown, idle, and busy readings.
  • Centralize tolerant activity reads for the picker and validator probes, returning unknown metrics instead of raising.
  • Add VRAM-only eligibility fallback, measured-idle preference, and explicit idleness evidence in reports.
  • Distinguish unavailable AMD SMI from the environment case where no GPU is idle.
.claude/skills/validate-kernel-pr/pick-idle-gpu.py
.claude/skills/validate-kernel-pr/validate_pr.sh
.claude/skills/validate-kernel-pr/report_schema.json
.claude/skills/validate-kernel-pr/SKILL.md
Restore the supplied worktree state after validation, including degraded exits and interrupts.
  • Track whether this process applied the patch independently from whether execution is in the baseline window.
  • Revert an applied patch during cleanup without re-applying it after a baseline run.
  • Cover normal, NO_GPU, and signal-termination paths while preserving the caller's original state.
.claude/skills/validate-kernel-pr/validate_pr.sh
.claude/skills/validate-kernel-pr/SKILL.md
Improve portability and direct execution of the idle-GPU picker.
  • Add an executable Python shebang.
  • Search ROCm >= 7.1 and legacy installation layouts, including versioned ROCm roots, for the AMD SMI bindings.
.claude/skills/validate-kernel-pr/pick-idle-gpu.py
Document and validate the new report semantics and degraded behavior.
  • Declare idleness_basis in the report schema.
  • Document optional activity metrics, skip meanings, and worktree restoration guarantees.
  • Verify fault-injected activity failures, busy and partial-failure selection, repeated runs, interrupts, portability layouts, and existing validator tests.
.claude/skills/validate-kernel-pr/SKILL.md
.claude/skills/validate-kernel-pr/report_schema.json
tests/test_validator.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

A target driven by a `__main__` guard could reach neither correctness_s1_grid nor
execution_receipt, so INCONCLUSIVE was the hard ceiling for every aiter
`op_tests/*.py` -- the repository's standard test shape. Neither skip described a
property of the target:

  * the route profiler is sys.setprofile plus a receipt write, and pytest was
    only where the hook happened to be installed;
  * the grid was injected solely through an environment variable, while these
    targets take shapes on the command line. That is a limit of the injector.

Reported as skips, both read as honest abstention while actually hiding a
capability gap -- the failure mode this skill exists to prevent.

The probe is now installed for script targets by run_script_with_probe.py, which
executes the file under runpy with run_name="__main__". It calls the probe's own
hooks rather than re-implementing the profiling, so `producer` stays truthful and
the tested PR still cannot forge a receipt. The probe module is generated whenever
a route is named, not only on the pytest branch; without that the runner had
nothing to import.

--shape-arg names the target's own CLI flag for the grid. The flag is named by the
caller rather than guessed, because a wrong guess appends argv the target silently
ignores. The hook is held to the same standard of proof as the env-var channel: the
flag must appear in an add_argument call, and the existing invalid-grid probe still
has to make the target fail before the stage is credited.

A receipt is now validated whenever a route was named, including when no grid was
configured or the grid channel could not be established. Two branches used to
abandon the receipt along with the grid, discarding evidence already collected. With
no grid it asserts route execution and nothing about shapes, which is all it is then
entitled to claim.

report_schema.json relaxes the PASS constraint `test_selection.runner: const pytest`
to `enum [pytest, script]`, since a script target can now supply both missing
stages. shape_arg is declared but deliberately not required, so reports from earlier
validators stay valid.

Verified on #4538 (gfx950, MI355 OAM), base 78b9440/891788643:

  * script target with --shape-arg -s: PASS 9/9, exit 0, review-pr consumable,
    receipt naming _auto_variant over the four injected production shapes
  * unaccepted --shape-arg flag: correctness_s1_grid skip, receipt still pass
  * seeded defect (fused -inf fill dropping [e, seq_len_kv)): BLOCK, exit 1, with
    correctness_s1_grid failing independently of the PR's own suite
  * tests/test_validator.py 24 passed; PASS/INCONCLUSIVE/BLOCK reports and one
    pre-shape_arg report all validate against the schema

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

@zhiding512
zhiding512 force-pushed the zhiding512/validate-kernel-pr-amdsmi-fix branch from 995af8b to 6de48d6 Compare August 31, 2026 08:36
@zhiding512

Copy link
Copy Markdown
Author

Superseded by ROCm#5142, which carries the same two commits (34c1617, 6de48d6) directly against ROCm/aiter:main and supersedes ROCm#4870. Closing this one; no further changes will be proposed against this fork.

@zhiding512 zhiding512 closed this Aug 31, 2026
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.

1 participant