Skip to content

fix(rdc): keep ROCr loaded across rdci diag checks - #13878

Merged
adam360x merged 2 commits into
developfrom
users/adam360x/ROCM-31379
Nov 3, 2026
Merged

adam360x merged 2 commits into
developfrom
users/adam360x/ROCM-31379

Conversation

@adam360x

@adam360x adam360x commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

QA reported that rdcd aborts during rdci diag -u -r 2 -g 1 on AMD Instinct MI455X (gfx1250) with ROCm build #265:

F0921 ... queue_controller.cpp:147] Could not find agent - 129205111608944

The checks of the ROCr diagnostic module (librdc_rocr.so: Compute Queue ready, System memory check) build test objects that call hsa_init() and hsa_shut_down() themselves. Nothing else held a reference, so ROCr was unloaded and loaded again between them.

rdcd always loads rocprofiler-sdk (the profiler module registers a tool at the first hsa_init(), even with RDC_DISABLE_ROCP=1), and rocprofiler-sdk does not support a ROCr reload. Depending on its version, the next queue creation aborts rdcd (Could not find agent) or fails, or the next profiler field read aborts it (HSA Queue is not initialized).

ROCm 10.2.0 and develop are affected too, and not only on MI455X: with a 10.2.0 nightly on MI350X the checks pass, but the next profiler field read aborts rdcd.

Keeping ROCr loaded exposed a second rocprofiler-sdk problem: it never destroys a queue it intercepted. With a queue per check, each rdci diag run left two queues and about 70 MB of VRAM in rdcd, until queue creation failed after 126 runs (MI455X allows 254 queues per process). So the checks now share one queue per GPU.

Technical Details

  • ROCr lifetime (RdcDiagnosticLib.cc): the module takes one hsa_init() reference at its first check and releases it in rdc_diag_destroy(); the test objects' calls only move the count. If that hsa_init() fails, the run fails (ROCr initialization failed) before any check is built, and the next run retries.
  • Shared queue: AcquireDiagQueue() creates one 128-packet queue per GPU, destroyed by rdc_diag_destroy() (the System memory check used to create a 131072-packet queue per run). A mutex runs one check at a time, since they share the queues (and MemoryAccess.cc a global kernel-argument pointer). The unused CreateQueue() helper is removed.
  • Cleanup (ComputeQueueTest.cc, MemoryAccess.cc, base_rocr_utils.cc, RdcRocrBase.*): both checks release their signals, memory, file descriptors and loaded code objects on every path, including Skip and failure (ScopeExit guard in common.h). A copy that fails after it started no longer leaks its signal.
  • gfx1250 kernels: without them both checks reported Skip on MI455X. They are code object v4 (ROCm 10.1 compiler: clang -x cl -Xclang -finclude-default-header -target amdgcn-amd-amdhsa -mcpu=gfx1250 -mcode-object-version=4 -O3), because the checks fill in the v4 kernel arguments; the default v6 fails the System memory check.
  • Tests: new GPU-less rdc_rocr_lifetime_tests runs the module against fake HSA calls that count loads, references and objects: ROCr is loaded once, one queue per GPU, a failed hsa_init() loads nothing and is retried, and no check leaves objects behind. It is built with the module, installed with the RDC test package, and run in RDC CI by tests/ci/test_gtest_runner.py. rdc_diag_kernel_tests requires v4 gfx1250 kernels.
  • CHANGELOG.md entries. No public API, proto or struct changes (the changed headers are not installed).

Trade-off: after the first rdci diag, rdcd keeps ROCr and one queue per GPU until it stops (MI455X: 3 KFD queues and about 149 MB of VRAM, flat across runs after the first), as it already does after the first profiler read. Changing the GPU partition mode then needs rdcd stopped, and a second rdci diag waits for the first.

#13570 and #13874 also edit CHANGELOG.md; trial merges show the three PRs merge cleanly in any order.

Not in this PR:

  • rocprofiler-sdk: support a ROCr reload and destroy the queues it intercepts (ROCM-33402). A process that creates and destroys a queue in a loop aborts after about 250 iterations under rocprofv3 --pmc.
  • Kernel arguments: the shipped gfx950 kernels (code object v6) and the gfx940-gfx942 binary search kernel (v5) declare 280-288 bytes of kernel arguments, while the checks fill in the 80-byte v4 layout. Release builds pass both checks on MI350X with them, but an assertion-enabled build aborts rdcd on the Compute Queue check's existing kernel-argument size assertion. v4 rebuilds match and pass on MI350X.

Issue Tracking

JIRA ID: ROCM-31379

Test Plan

  • 1x MI455X (gfx1250), Ubuntu 26.04: QA's build Stop using architecture specific intrinsics in favour of libSIMDe #265, ROCm 10.2.0, and a 10.2.0 BKC build of 2026-10-13. Each experiment runs its own rdcd -u in a private network namespace: N rdci diag -u -r 2 -g 1 runs, a profiler rdci dmon (fields 805/806), one more diag; also profiler first. KFD queues and VRAM from /sys/class/kfd/kfd/proc/<pid>/. The module is built from this branch against each ROCm.
  • 8x MI350X (gfx950), Ubuntu 24.04, 10.2.0 nightly of 2026-10-22: on GPU 0, the stock module and this PR's module (release and assertion-enabled builds, shipped gfx950 kernels and v4 rebuilds); on a group of all 8 GPUs, this PR's release build with the shipped kernels, in both orders.
  • rdc_rocr_lifetime_tests and rdc_diag_kernel_tests (GCC 11) on this branch, against develop's module, and with each part of the fix removed; the CI wrapper; pre-commit.

Test Result

MI455X rdci diag runs KFD queues / VRAM, run 1 → last profiler after rdcd exit
#265 stock aborts in run 1 (Could not find agent) - - 134
10.2.0 stock run 1 Skip (no gfx1250 kernels), run 2 fails queue creation - aborts 134
BKC 10-13 stock aborts in run 1 - - 134
10.2.0, ROCr kept loaded, a queue per check 126 Pass, then Fail 4 → 254 / 0.39 → 9.3 GB aborts 134
10.2.0, this PR 141/141 Pass 3 → 3 / 149 → 149 MB works 0
#265, this PR 21/21 Pass 3 → 3 / 149 → 149 MB works 0
BKC 10-13, this PR 21/21 Pass 3 → 3 / 287 → 149 MB works 0
  • Pass = both checks Pass. Profiler read first: 3/3 on Stop using architecture specific intrinsics in favour of libSIMDe #265 and on the BKC build. Stock builds run clean with HSA_TOOLS_DISABLE_REGISTER=1 (rocprofiler-sdk not registered with ROCr). On the BKC build rdci itself exits with SIGSEGV after printing (the client exit crash fixed on develop by fix(rdc): keep librdc_client loaded so rdci does not crash on exit #13296), which does not affect rdcd.
  • 8x MI350X: stock passes 3/3 diags on GPU 0, then the profiler read aborts rdcd (HSA Queue is not initialized, exit 134). This PR (release build, shipped kernels): 4/4 on GPU 0 and 11/11 on all 8 GPUs, every GPU passing both checks, including a run after the profiler read; 3/3 on all 8 GPUs with the profiler read first. KFD queues stay flat across runs (24 with all 8 GPUs) and rdcd exits 0.
  • gfx1250 kernels: v4 passes both checks; v6 fails the System memory check in a release build.
  • rdc_rocr_lifetime_tests 5/5 pass and 4 of 5 fail against develop's module; each of 14 mutants of the fix, and reverting either review fix, makes a test fail. The mutex is not unit-tested. rdc_diag_kernel_tests, the CI wrapper and pre-commit pass.

Submission Checklist

rdcd aborted during "rdci diag" on AMD Instinct MI455X (gfx1250):

  F0921 ... queue_controller.cpp:147] Could not find agent - 129205111608944

Each check of the ROCr diagnostic module (Compute Queue ready, System
memory check) builds test objects that call hsa_init() when they are
built and hsa_shut_down() when they are destroyed. Nothing else held a
reference, so every test object unloaded the ROCr runtime and the next
one loaded it again. rdcd also loads rocprofiler-sdk, because the
profiler module registers a tool with it, and rocprofiler-sdk keeps the
agents and queues of the first ROCr load it sees. After a reload its
queue interception no longer knows the GPU agent: depending on the
rocprofiler-sdk version and on where the new agents land in memory, the
next queue creation aborts rdcd ("Could not find agent"), queue creation
fails with HSA_STATUS_ERROR_OUT_OF_RESOURCES, or the next profiler field
read aborts rdcd ("HSA Queue is not initialized"). With ROCr's
rocprofiler registration turned off (HSA_TOOLS_DISABLE_REGISTER=1) the
same runs are clean.

- The module takes one hsa_init() reference at its first check and
  releases it in rdc_diag_destroy(), so the test objects' hsa_init() and
  hsa_shut_down() calls only move the reference count. A failed
  hsa_init() is retried at the next check.
- The checks share one queue per GPU, which the module creates at the
  first check that needs it and destroys with its reference, and the
  module runs one check at a time. rocprofiler-sdk does not destroy a
  queue it intercepted when the application destroys it, so with ROCr
  kept loaded every check's own queue stayed in rdcd: on MI455X with
  ROCm 10.2.0 each run added two queues and about 70 MB of VRAM, and
  after 126 runs queue creation failed and the next profiler field read
  aborted rdcd.
- With ROCr no longer unloaded after each check, nothing else releases
  what a check leaves behind. Both checks now release their signals,
  memory and loaded code objects on every path, including when they
  fail or are skipped; before, a skipped check left its queue and memory
  allocated.
- RDC shipped no diagnostic kernels for gfx1250, so both checks reported
  Skip there. Add them, built as code object v4: the checks fill in the
  kernel arguments of the v4 layout, and a later code object version
  takes the work-group size from hidden arguments the checks never
  write, which fails the System memory check's comparison.

Tests:
- rdc_rocr_lifetime_tests (new, GPU-less): compiles the module against
  fake HSA runtime calls and checks that repeated checks load ROCr once
  and create one queue, that rdc_diag_destroy() releases both, that a
  failed hsa_init() is retried, and that skipped, passing and failing
  checks leave no signal, memory or code object behind. Three of its
  four tests fail on the old module. It runs in the rdc_test CI job
  through test_gtest_runner.py.
- rdc_diag_kernel_tests checks that the gfx1250 kernels ship and are
  code object v4.

Signed-off-by: Adam360x <Adam.pryor@amd.com>
@adam360x
adam360x requested review from a team as code owners October 21, 2026 04:06
Copilot AI balanced review requested due to automatic review settings October 21, 2026 04:06
@therock-pr-bot

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Initialization failure can still trigger ROCr reloads, and asynchronous copy failures can leak signals.

2 open issues
What changed in this PR

This PR stabilizes RDC diagnostics by retaining ROCr between checks, reusing GPU queues, and improving resource cleanup.

Changes:

  • Retain the runtime and serialize diagnostics using shared per-GPU queues.
  • Release diagnostic resources on success and failure paths.
  • Add lifetime regression tests, CI integration, and gfx1250 kernel checks.
File Description
projects/​rdc/​tests/​unit/​test_rocr_lifetime.cc Adds GPU-less lifetime and cleanup tests.
projects/​rdc/​tests/​unit/​test_diag_kernels.cc Checks gfx1250 kernel packaging and version.
projects/​rdc/​tests/​unit/​CMakeLists.txt Builds and installs lifetime tests.
projects/​rdc/​tests/​ci/​test_gtest_runner.py Runs lifetime tests in CI.
projects/​rdc/​rdc_libs/​rdc_modules/​rdc_rocr/​RdcRocrBase.cc Initializes owned kernel handles.
projects/​rdc/​rdc_libs/​rdc_modules/​rdc_rocr/​RdcDiagnosticLib.cc Retains ROCr, shares queues, and serializes checks.
projects/​rdc/​rdc_libs/​rdc_modules/​rdc_rocr/​MemoryAccess.cc Adds scoped cleanup and queue reuse.
projects/​rdc/​rdc_libs/​rdc_modules/​rdc_rocr/​ComputeQueueTest.cc Improves cleanup and reuses module queues.
projects/​rdc/​rdc_libs/​rdc_modules/​rdc_rocr/​base_rocr_utils.cc Tracks and releases loaded kernel resources.
projects/​rdc/​include/​rdc_modules/​rdc_rocr/​RdcRocrBase.h Adds kernel-resource ownership fields.
projects/​rdc/​include/​rdc_modules/​rdc_rocr/​ComputeQueueTest.h Tracks executable and reader handles.
projects/​rdc/​include/​rdc_modules/​rdc_rocr/​common.h Adds scope guard and shared-queue declaration.
projects/​rdc/​CHANGELOG.md Documents fixes and runtime-lifetime trade-offs.

Review effort: Balanced

Follow-ups for the ROCr diagnostic module's lifetime fix:

- When the module's own hsa_init() failed, the checks still ran and
  called hsa_init() themselves. If a check's call succeeded, that check
  could hold the last reference and unload ROCr when it ended, so the
  next run loaded it again: the reload that rocprofiler-sdk cannot
  handle. A run now fails with "ROCr initialization failed" before it
  builds a check, and the next run tries again.
- The Compute Queue check's memory copy destroyed its signal only when
  the copy did not start. When a started copy fails, the runtime sets
  the signal negative and the check threw without destroying it. With
  ROCr kept loaded nothing else releases it, so destroy it there too.
- Remove CreateQueue(), which nothing calls. Checks take their queue
  from AcquireDiagQueue(): rocprofiler-sdk never frees a queue it
  intercepted, so a queue per check stays in rdcd.

rdc_rocr_lifetime_tests: the failed-init test now requires that the
failed run loads, unloads and shuts down nothing and that the retry
loads ROCr once; a new test fails a started copy and requires that its
signal is destroyed. Each fails against the previous version of the
file it covers.

Signed-off-by: Adam360x <Adam.pryor@amd.com>
@adam360x
adam360x merged commit f75433d into develop Nov 3, 2026
75 of 78 checks passed
@adam360x
adam360x deleted the users/adam360x/ROCM-31379 branch November 3, 2026 14:33
@systems-assistant

Copy link
Copy Markdown
Contributor

TheRock Submodule Bump Activity

Newest first

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants