Skip to content

UCP/CUDA: Prepare dormant async GDRCopy short copies - #12157

Open
nbellalou wants to merge 1 commit into
masterfrom
nv4741160-async-gdr-short-copies
Open

nbellalou wants to merge 1 commit into
masterfrom
nv4741160-async-gdr-short-copies

Conversation

@nbellalou

Copy link
Copy Markdown
Contributor

What?

Second of three PRs to enable gdr_copy for buffers allocated with cudaMallocAsync, following merged #12092.

This PR adds the complete short-copy routing and registration path, but leaves it dormant in normal use. The third PR will connect allocation detection, runtime qualification and the enable/disable setting.

Why?

Stream-ordered CUDA allocations need a targeted route to GDRCopy's DMA-BUF mapping path without making other transports newly eligible or changing the buffer's outward memory type. Preparing the complete path before activating it keeps this PR safe to merge on its own.

How?

  • Introduce the CUDA-async memory flag and the matching MD capability together with their readers. Select an async candidate only for GET/PUT short operations, and retain all required memory flags except legacy GDR_CAPABLE on that qualified route.
  • Register an aligned region quietly, presenting it as CUDA only for this local registration. If registration fails, exclude the failed MD and use the existing route before any copy starts.
  • Preserve fatal/no-retry handling once a GET/PUT short operation has been issued; do not risk retrying a partially completed copy.
  • Use the operation-specific lane for qualified async short-copy cost estimates. Preserve the existing ordinary-buffer, zero-copy and unknown-remote-memory decisions.
  • Add routing/isolation and cost-model tests, plus GET/PUT operation-failure tests that check that no retry occurs.

Scope

No production allocation detector emits the new marker, and no MD advertises the new capability in this PR. The qualification helper from #12092 still has no production caller. There is no new configuration setting, global reclassification, or change to other transports' eligibility.

The path can be exercised by tests that inject the marker and capability, but it does not enable async GDRCopy for applications. No measured zero-overhead or latency/throughput claim is made.

Validation

  • Native A100 developer build with GDRCopy 2.6: passed.
  • Focused GDRCopy, async routing, datatype, registration and memory-flag suite: 76 executed passes, 1 C2C hardware skip.
  • All four added routing/cost/GET-error/PUT-error tests executed and passed.
  • One commit on merged UCT/CUDA: Prepare GDRCopy support for cudaMallocAsync #12092; 11 files, +440/-34. Rebase preserved the original PR2 patch exactly; whitespace checks passed.
  • No new R620 enabled-copy acceptance run is claimed for this dormant PR.
  • Known follow-up: the custom formatter reports formatting-only differences in the prepared patch. This publication retains the unchanged rebased implementation.

@svc-nvidia-pr-review

Copy link
Copy Markdown

🤖 Starting review — findings will be posted here when done.

@svc-ucx

svc-ucx commented Oct 25, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (Tests BlueField on worker 0) · commit b5b6bc1b

TL;DR: The build (not the tests) failed while linking test/mpi/shmem_pingpong — the HPC-X OpenMPI liboshmem.la on the BlueField worker drags in -liberty from a custom binutils path that doesn't exist on that machine, so ld fails. This is an agent/toolchain environment problem, unrelated to PR #12157; fix it by making the binutils/libiberty path available (or disabling the BFD dependency) on the BlueField agent.

Full analysis

Summary: BlueField on worker 0 failed in ./contrib/test_jenkins.sh during make all in build-test/test/mpi with /bin/ld: cannot find -liberty: No such file or directory → make[2]: *** [Makefile:727: shmem_pingpong] Error 1.

Root cause: Pure link-time environment breakage, not a code defect:

  • At 14:44:25 libtool emitted libtool: warning: library '/hpc/local/benchmarks/daily/ga/2026-10-06/hpcx-gcc-ubuntu22.04/ompi/lib/liboshmem.la' was moved. — the stale/moved .la file still carries hard-coded dependency_libs.
  • The resulting link line for shmem_pingpong therefore contains -L/hpc/local/oss/binutils/2.37/ubuntu22.04/lib -L/hpc/local/oss/binutils/2.37/ubuntu22.04/lib64 -L/tmp/binutils-gdb/zlib -lpthread -lz -liberty ....
  • Those are paths from a custom binutils build (the -lbfd/-liberty/-lz chain used by UCS backtrace support, see commit 29aa1f2 "UCS/BUILD: Support custom path for binutils dependency"). libiberty.a is not present/mounted on this BlueField agent (note -L/tmp/binutils-gdb/zlib, a temp dir that no longer exists), so -liberty cannot be resolved.
  • Everything before this point compiled and linked cleanly (libucs, libuct, libucp, ucx_info, ucx_perftest, io_demo all built), and the log shows continuous activity with no stalls — so this is not a hang or a timeout, and no test ever ran. Nothing in the failure touches the GDR short-copy code in this PR.

Implicated commit: unknown — not caused by commit [REDACTED:Hex High Entropy String] (nv4741160-async-gdr-short-copies). The relevant historical change is 29aa1f2 (Yossi Itigin, "UCS/BUILD: Support custom path for binutils dependency"), which introduced the custom-binutils link dependency, but the trigger is the HPC-X daily install ga/2026-10-06 on the BlueField agent.

File: build-test/test/mpi/Makefile:727 (generated from test/mpi/Makefile.am, target shmem_pingpong); dependency comes from /hpc/local/benchmarks/daily/ga/2026-10-06/hpcx-gcc-ubuntu22.04/ompi/lib/liboshmem.la

Suggested fix:

  1. Infra first: on the BlueField agent (agent-02), restore/mount /hpc/local/oss/binutils/2.37/ubuntu22.04/lib{,64} containing libiberty.a, or install the distro binutils-dev/libiberty-dev package; and refresh the HPC-X daily module so the relocated liboshmem.la no longer points at /tmp/binutils-gdb/zlib.
  2. Then re-run the PR build — it should pass without any code change.
  3. Hardening (optional, to stop a moved HPC-X .la from breaking every PR on BlueField): configure the BlueField job with the BFD/backtrace dependency off (e.g. --without-bfd/--disable-backtrace-detail, whichever the local configure.ac exposes) so the -lbfd -liberty -lz chain is not propagated into MPI test links, or skip building test/mpi in the BlueField flow since it only needs the MPI pingpong helpers.

Related: none found — no existing issue/PR in this repo matches cannot find -liberty / shmem_pingpong link failure.

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id db9b2ac9-c855-485e-92ac-ffd8b05e3ab8 in the triage console for the audit trail.

UCS_MEM_FLAG_GDR_CAPABLE = UCS_BIT(2),

/** Memory originates from a CUDA stream-ordered allocation. */
UCS_MEM_FLAG_CUDA_ASYNC = UCS_BIT(3)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nothing in the tree sets this flag, and no MD sets UCT_MD_FLAG_CUDA_ASYNC_MEMTYPE_COPY either, so the whole new route is unreachable except in the gtest that patches md_attr directly. are the producers (cuda_copy mem-flag detection and gdr_copy md_query using the existing uct_gdr_copy_cuda_async_detect()) planned for a follow-up PR?

Comment thread src/ucp/dt/dt.c
ucs_status_t status;

if ((mem_info == NULL) &&
(ucp_worker_cuda_async_ep_for_short(worker, &detected, operation,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this probe (the mem_info == NULL probe) passes only the fabricated UCS_MEM_FLAG_CUDA_ASYNC, so an MD whose required_mem_flags also contain e.g. UCS_MEM_FLAG_REGISTRABLE never passes ucs_test_all_flags() here, and detection (hence the async route) is silently skipped for it. why not probe with all mem flags set and let the second call do the real flag matching?

Comment thread src/ucp/dt/dt.c
}

async_ep = ((mem_info == NULL) ||
!(mem_info->flags & UCS_MEM_FLAG_CUDA_ASYNC)) ? NULL :

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

minor: ucp_worker_cuda_async_ep_for_short() already filters by mem type and by UCS_MEM_FLAG_CUDA_ASYNC, so the extra flag test here is redundant - (mem_info == NULL) ? NULL : ucp_worker_cuda_async_ep_for_short(...) is enough.

Comment thread src/ucp/dt/dt.c
ucs_align_ptr_range(&reg_address, &reg_length,
ucp_ep_md_attr(async_ep, lane)->reg_alignment);
status = ucp_mem_type_reg_buffers(
worker, reg_address, reg_length, UCS_MEMORY_TYPE_CUDA,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why register as UCS_MEMORY_TYPE_CUDA when the buffer may be CUDA_MANAGED? if this md is in context->cache_md_map[], ucp_mem_type_reg_buffers() goes through ucp_memh_get(), and ucp_memh_find_slow() asserts the cached region mem_type equals the requested one - a managed buffer already cached as CUDA_MANAGED would trip that assert.


if (worker->mem_type_ep[local_mem_type] != NULL) {
ep_config = ucp_ep_config(worker->mem_type_ep[local_mem_type]);
copy_mem_info = local_mem_info;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

copy_mem_info follows whichever side happens to have a mem_type ep, so for the mtype "local copy" (cuda frag vs cuda user buffer) the frag flags win and the user buffer's async flag is dropped: the estimate keeps rma_lanes[0] while the runtime copy takes the async lane. should the async check consider both mem_infos?

m_counts{0, 0}
{
ucs_assert(m_active == nullptr);
ucs_assert(m_ifaces[0]->internal_ops != m_ifaces[1]->internal_ops);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ucs_assert() is compiled out in release builds, and then m_orig[1] gets the hook itself if both ifaces share internal_ops, so estimate_perf() recurses forever. pls make this a real check in the test (skip/fail) rather than an assert, and consider copying internal_ops and swapping the pointer instead of writing into the per-TL table shared by all ifaces of that transport.

auto saved = ucs_global_opts.handle_errors;
ucs_global_opts.handle_errors = 0;
if (operation == UCT_EP_OP_GET_SHORT) {
EXPECT_DEATH(ucp_mem_type_pack(worker(), buffer, buffer, 64,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this is the first death test in the gtest suite; EXPECT_DEATH forks a child that already has an initialized CUDA context and open UCT ifaces, which is not supported and tends to hang or die with an unrelated message. can we assert the "no retry" behavior without killing the process (e.g. check lane/ep selection and registration), or drop these two cases?

@svc-nvidia-pr-review

Copy link
Copy Markdown

Residual coverage gap: No test exercises the async route end to end with a real stream-ordered allocation, because no MD advertises the new capability; the new tests only validate the selection helper and the perf model with hand-patched md_attr.

Residual coverage gap: test_short_copy_perf() feeds a remote_mem_info carrying UCS_MEM_FLAG_CUDA_ASYNC, but ucp_proto_init_add_buffer_perf() now forces recv_mem_info.flags = 0, so that input cannot occur in production.

Note (not flagged): rndv_rtr.c now derives the "unpack copy" local type from params.super.reg_mem_info instead of frag_mem_type; this is equivalent only because UCS_MEMORY_TYPE_HOST is bit 0 and is the one type that may skip ucp_mm_get_alloc_md_index() success - a hidden dependency on iteration order.

This branch has not been deployed

No deployments
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.

3 participants