Skip to content

fix(serve): close serve races and translate GPU selection through visibility masks (EAI-7194) - #267

Open
r0x0r wants to merge 3 commits into
mainfrom
fix-race-conditions
Open

fix(serve): close serve races and translate GPU selection through visibility masks (EAI-7194)#267
r0x0r wants to merge 3 commits into
mainfrom
fix-race-conditions

Conversation

@r0x0r

@r0x0r r0x0r commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes the concurrency races in rocm serve and completes the visible-ordinal-correctness half of EAI-7194, so GPU selection is honest under an active visibility mask. The branch is two focused commits:

  1. 66e6ee5 — close daemon-autostart, GPU-select, and managed-launch races. Adds a FileLock primitive plus two lock files so concurrent serves cannot autostart the daemon twice, race the GPU-selection read, or interleave the managed-launch record write.
  2. ccff388 — translate GPU selection through active visibility masks. serve GPU selection and --gpu validation are now interpreted against the devices visible under the active HIP_VISIBLE_DEVICES / ROCR_VISIBLE_DEVICES mask instead of the raw amd-smi ordinal space.

What changed (visible-ordinal correctness)

  • serve() computes the visible device set once via usable_amd_gpu_indices() and threads it through resolve_gpu_indices, reusing it for both the no-usable-GPU fail-fast and selection.
  • Auto-select restricts candidates to the visible set; when every visible device is busy it falls back to the lowest visible ordinal instead of a hardcoded 0.
  • An explicit --gpu N that is in range but hidden by the active mask is now rejected up front with an actionable message, rather than being silently reinterpreted by the runtime onto a different device.
  • The ROCR advisory is narrowed to describe the mask-relative interpretation, and the launch-lock critical-section comment is corrected.

Deferred (intentional)

Full ROCR physical→HIP export translation is not included. That is a cross-engine contract change (rocm-engine-protocol::apply_gpu_visibility and every engine) on the GPU-required path that cannot be verified without multi-GPU hardware under an active ROCR mask, where a wrong translation would misroute serves to the wrong device. The narrowed advisory remains until that work can be done and verified on hardware.

Testing

  • New unit tests: mask-aware auto-select (candidate restriction, lowest-visible fallback, all-masked → none) and --gpu mask rejection.
  • Launch-lock regression test now drives the real resolve_gpu_indices entry point serve() uses.
  • New e2e Scenario 15 asserts a mask-hidden --gpu is refused. It is exercised on the multi-GPU e2e-gpu lane (on single-GPU lanes the same request is out of range — both are honest refusals); the no-GPU mock lane skips it via @requires-gpu.
  • Local gates: cargo test -p rocm (458/458 serial), cargo clippy --workspace --all-targets -D warnings clean, scripts/smoke_local.py ok.

GPU-hardware verification of Scenario 15 will run on the e2e-gpu lane in CI.

@r0x0r
r0x0r requested a review from a team as a code owner August 17, 2026 08:37
@r0x0r
r0x0r force-pushed the fix-race-conditions branch from 599630c to 4a1e4c3 Compare August 18, 2026 11:01
r0x0r added 3 commits August 21, 2026 12:27
…ces (EAI-7194)

Three concurrency races existed in the serving path:

1. Daemon autostart was a check-then-spawn TOCTOU: two concurrent callers
   could both read "not running" and each spawn a background daemon.
2. GPU --gpu auto read the busy-GPU list without a lock, so two concurrent
   serves could select the same idle GPU.
3. The read-select-launch sequence for managed serving was not atomic: a
   second serve could target the same GPU between the select and the claim.

Add a cross-process advisory lock primitive (rocm_core::FileLock, backed by
std File::lock) and two lock files:

- automation autostart lock: ensure_background_helper_running_quiet now
  re-checks liveness under the lock before spawning, so only the first holder
  starts the daemon.
- managed-launch lock: serve() holds it across GPU auto-selection and the
  claiming service-record write (dropped before the readiness wait / attached
  stream), making select-then-claim atomic so concurrent serves land on
  distinct GPUs.

Tests: FileLock mutual-exclusion / parent-dir / independence unit tests, and
a launch_lock_makes_gpu_select_and_claim_atomic regression that races two
select-then-claim sequences and asserts distinct GPUs. The behavior only
diverges under concurrent invocation, which the single-process cucumber e2e
harness cannot exercise deterministically, so no Gherkin scenario is added.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
…EAI-7194)

Complete the visible-ordinal-correctness half of EAI-7194: `rocm serve` GPU
selection and `--gpu` validation are now interpreted against the devices
visible under the active HIP_VISIBLE_DEVICES/ROCR_VISIBLE_DEVICES mask, not the
raw amd-smi ordinal space.

- serve() computes the visible device set once via usable_amd_gpu_indices() and
  threads it through resolve_gpu_indices, reusing it for both the no-usable-GPU
  fail-fast and selection.
- auto-select restricts candidates to the visible set and, when every visible
  device is busy, falls back to the lowest VISIBLE ordinal instead of a
  hardcoded 0.
- an explicit `--gpu N` that is in range but hidden by the active mask is now
  rejected up front with an actionable message, rather than being silently
  reinterpreted by the runtime onto a different device.
- narrow the ROCR advisory to describe the mask-relative interpretation and
  correct the launch-lock critical-section comment.

Full ROCR physical->HIP export translation is intentionally deferred: it is a
cross-engine contract change (rocm-engine-protocol::apply_gpu_visibility and
every engine) on the GPU-required path that cannot be verified without
multi-GPU hardware under an active ROCR mask, where a wrong translation would
misroute serves. The narrowed advisory remains until then.

Tests: mask-aware auto-select/validation unit tests; launch-lock regression now
drives resolve_gpu_indices; e2e Scenario 15 asserts a mask-hidden `--gpu` is
refused (exercised on the multi-GPU e2e-gpu lane).

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Run `cargo fmt --all` over the two preceding EAI-7194 commits so the prek
`cargo fmt --all --check` hygiene hook passes. No functional change.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
@r0x0r
r0x0r force-pushed the fix-race-conditions branch from 4a1e4c3 to e6fb3bd Compare August 21, 2026 12:29
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