Skip to content

UCT/ZE/COPY: Initialize memtype cache when opening MD - #12069

Merged
tvegas1 merged 6 commits into
openucx:masterfrom
intel-staging:fix/ze-copy-memtype-cache-eager
Oct 30, 2026
Merged

tvegas1 merged 6 commits into
openucx:masterfrom
intel-staging:fix/ze-copy-memtype-cache-eager

Conversation

@yafshar

@yafshar yafshar commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

What?

Create the global memtype cache when the ze_copy memory domain is opened, as cuda_copy already does (cuda_copy_md.c, "install its handler before CUDA events"). Add a ze_copy MD gtest which checks that the cache exists right after uct_md_open() and that a Level Zero allocation made by the application afterwards is found in it. The ze_copy MD tests now use the shared test_md fixture.

Why?

If nothing else has initialized the global memtype cache, it is created on the first lookup. A Level Zero allocation made before that has no handler to receive its UCM MEM_TYPE_ALLOC event, and in the reproduced case UCM's scan of /proc/self/maps did not recover it, since the device VA has no mapping in the process. Without a later memory query to populate the cache, the lookup misses and UCP assumes host memory. Depending on the protocol this ends in the ucp_mm.c assertion mem_type == memh->mem_type (mem_type=host got memh->mem_type=ze-device) or a segfault in the host eager-short path.

A successful DMA-BUF query can mask the bug, because uct_ze_copy_md_mem_query() updates the cache as a side effect. Paths without such a query hit it; UCX_TLS=sm,ze_copy,ze_ipc is a reproducer on master today.

Verified on Intel Arc Pro B60 and B70 nodes:

  • ucx_perftest -t tag_bw -m ze-device -s 4M with UCX_TLS=sm,ze_copy,ze_ipc (two processes, one device, B60): asserts on master, passes with this change.
  • UCX_TLS=rc,ze_copy, device-to-device over the NIC, tag_bw/ucp_put_bw/ucp_get at 8 B, 64 KiB, 4 MiB and -V pingpong validation (B60): all pass, as before.
  • The new test fails without the fix (ucs_memtype_cache_global_instance is NULL after uct_md_open()) and passes with it, on B60 and B70. The ze_cpy, ze_copy and memtype cache gtests pass on both, except test_memtype_cache_deferred_create for ze-device, which already fails on master.

How?

One call to ucs_memtype_cache_global_create() at the end of uct_ze_copy_md_open().

The regression test resets the global cache (ucs_memtype_cache_cleanup() + ucs_memtype_cache_global_init(), the same pattern as test_memtype_cache_deferred_create) in the fixture's init(), before test_md::init() opens the MD. An earlier test in the same process may already have created the cache on demand, and without the reset the test would pass regardless of the fix. The test runs with UCX_MEMTYPE_CACHE=y, so a failure to create the cache fails it instead of skipping, and it allocates through mem_buffer, as an application would.

The ze_copy MD tests now derive from test_md, so they are only instantiated where a ze_cpy MD exists; CI without Intel GPUs runs none of them rather than reporting skips. component_is_registered is removed because it only compared the name stored in the component struct and would pass even if the component were not registered. query_md_resources_returns_at_least_one is removed because under test_md it would only be instantiated after a ze_cpy resource has been found.

Without an earlier cache lookup, a Level Zero allocation can occur
before the UCM handler is installed. A subsequent cache miss makes
UCP assume host memory, which can trigger the ucp_mm.c memory-type
assertion or a crash in the host eager-short path. Create the cache
at ZE copy MD open, as CUDA copy does.

This fixes ZE-device transfers on paths without a DMA-BUF
registration query, such as UCX_TLS=sm,ze_copy,ze_ipc.

Signed-off-by: Yaser Afshar <yaser.afshar@intel.com>
@svc-nvidia-pr-review

Copy link
Copy Markdown

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

Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
@svc-nvidia-pr-review

Copy link
Copy Markdown

Residual coverage gaps: the test only runs on a host with a working Level Zero runtime and device, and there is no CI evidence in the diff that it was exercised, so the ZE path is validated by hardware-gated tests only. Also, only UCS_MEMORY_TYPE_ZE_DEVICE is covered; ZE_HOST/ZE_MANAGED allocations go through the same hook but are untested.

Signed-off-by: Yaser Afshar <yaser.afshar@intel.com>
@svc-nvidia-pr-review

Copy link
Copy Markdown

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

Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
@svc-nvidia-pr-review

Copy link
Copy Markdown

test/gtest/uct/ze/test_ze_copy_md.cc

Residual coverage gap: only UCS_MEMORY_TYPE_ZE_DEVICE is exercised; ZE_HOST/ZE_MANAGED go through the same hook and md_open path but stay untested.

test/gtest/uct/ze/test_ze_copy_md.cc

Residual coverage gap: the whole file is hardware-gated (working Level Zero runtime + device), so there is no CI evidence in the diff that the new test actually ran.

Signed-off-by: Yaser Afshar <yaser.afshar@intel.com>
@svc-nvidia-pr-review

Copy link
Copy Markdown

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

Signed-off-by: Yaser Afshar <yaser.afshar@intel.com>
Signed-off-by: Yaser Afshar <yaser.afshar@intel.com>
@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@svc-ucx

svc-ucx commented Oct 19, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (Tests roce on worker 0) · commit bffc39c8

TL;DR: ucx_info -epw -u t hung for 3m08s with zero output and was still alive when the agent killed the job — the hang starts exactly where this PR's only functional change takes effect: the new eager ucs_memtype_cache_global_create() call in uct_ze_copy_md_open(), which now installs UCM memory hooks (module dlopen + mmap patching + /proc/self/maps existing-alloc scan) from inside MD open. Make the memtype-cache creation lazy again (or move it out of the md_open path) rather than calling it per-MD-open.

Full analysis

Summary: The "Tests roce on worker 0" job's test_unused_env_var step hung in ucx_info; the Azure agent cancelled the job after ~3 minutes of complete silence and reported ucx_info (pid 496811) plus two grep children as orphan processes.

Root cause: This is a hang, not a slow test. Timeline evidence from the step log:

  • 16:24:34.2401 — + ./src/tools/info/ucx_info -epw -u t
  • 16:24:34.2420 — + grep unused (last application output)
  • 16:27:42.2508 — ##[error]The agent has received a shutdown signal

That is a single ~3m08s gap with no output at all, i.e. essentially the entire remaining runtime, and the cleanup section confirms ucx_info was still resident and had to be terminated. Everything before it (configure, build, make install through src/uct/ze) completed normally in seconds, so the build is fine and the regression is in library runtime behaviour during ucx_info's MD/transport enumeration.

The only functional change on this branch is in the ZE copy MD. src/uct/ze/copy/ze_copy_md.c:345-347 now does:

/* Install the memtype cache handler before Level Zero allocations. */
ucs_memtype_cache_global_create();
return UCS_OK;

Previously the global memtype cache was created lazily, on the first ucs_memtype_cache_lookup() (memtype_cache.c:349). Creating it eagerly from inside md_open pulls a very heavy, side-effect-laden initialization into the MD-open path: ucs_memtype_cache_global_create() → ucs_memtype_cache_get_global() → UCS_CLASS_NEW(ucs_memtype_cache_t) → ucm_set_event_handler(UCM_EVENT_MEM_TYPE_ALLOC|FREE|UCM_EVENT_FLAG_EXISTING_ALLOC, ...) (memtype_cache.c:413-417) → ucm_event_install(), which runs ucm_mmap_install() (bistro/reloc patching of mmap/munmap), UCS_MODULE_FRAMEWORK_LOAD(ucm, ...) — a dlopen sweep of all UCM modules (src/ucs/sys/module.c:299-359) — and then, because of UCM_EVENT_FLAG_EXISTING_ALLOC, calls every installer's get_existing_alloc() (event.c:614-618), which walks existing allocations.

So ucx_info, while iterating UCT components and opening MDs, now performs global hook installation and dlopen/maps-scanning under the UCM event lock (ucm_event_lock, a pthread_rwlock acquired in write mode by ucm_event_handler_add, event.c:502) and the non-recursive ucs_memtype_cache_global_instance_lock spinlock (memtype_cache.c:25, 81-90). A pthread spinlock has no timeout and no diagnostics — a re-entrant or inverted acquisition on that path spins forever, which matches the symptom precisely: process alive, CPU burning, not one byte of output for 3 minutes. Note the code comment at memtype_cache.c:68-69 explicitly warns that cache creation must stay outside the lock "to avoid a Coverity error of lock inversion with UCS_INIT_ONCE from ucm_set_event_handler()" — the PR adds a new caller into exactly this fragile region.

Implicated commit: 0157e44d "UCT/ZE/COPY: Initialize memtype cache when opening MD" — Yaser Afshar (the tested HEAD bffc39c8, "UCT/ZE/COPY: Clarify memtype cache handler comment", only touches the comment above the offending line).

File: src/uct/ze/copy/ze_copy_md.c:345-347 (the ucs_memtype_cache_global_create() call in uct_ze_copy_md_open); supporting path src/ucs/memory/memtype_cache.c:96-99 and :56-94.

Suggested fix:

  1. Drop the eager call from md_open. Revert ze_copy_md.c:345-347 and rely on the existing lazy creation in ucs_memtype_cache_lookup(). uct_ze_copy_md_mem_query() already calls ucs_memtype_cache_update() (line 217), which is a safe no-op until the cache exists, so correctness of ZE memtype caching does not require pre-creating it during md_open. Note that neither uct_cuda_copy_md_open nor the ROCm MD does this — ZE would be the only transport installing UCM hooks from md_open, which is why only this branch hangs.
  2. If eager installation is genuinely needed so the handler is registered "before Level Zero allocations", do it from the ZE module/component init (e.g. UCS_MODULE_INIT() in src/uct/ze/base/ze_base.c:550) or a one-shot UCS_INIT_ONCE guarded by uct_ze_base_init() success — not once per MD open, and not while UCT is mid-enumeration.
  3. Harden the test so a hang fails fast instead of killing the agent: wrap the ucx_info invocation in test_unused_env_var with timeout 60 (the script already defines a TIMEOUT variable at contrib/test_jenkins.sh:43-46 but does not apply it here). This converts a 3-minute agent-level cancellation into an actionable test failure.
  4. To confirm before merging: run timeout 30 ucx_info -epw -u t on a ZE-enabled host under gdb -p / pstack, and check whether the stuck thread is inside pthread_spin_lock under ucs_memtype_cache_get_global or inside ucm_event_install/dlopen. That pinpoints which of the two locks is the spinner and tells you whether a recursive-spinlock conversion is also warranted in memtype_cache.c.

Related: PR #12069 (this PR, "UCT/ZE/COPY: Initialize memtype cache when opening MD"); adjacent ZE work in PR #11903 ("UCT/ZE/COPY: Allocate DMA-BUF-exportable memory for RDMA"). Historical context for the locking fragility: commit 1c088748 "UCP/UCS/UCT: Use spinlock for memtype cache" and 119a81b0 "UCM/UCS: Fail to create memtype cache if cannot patch Cuda driver API".

@tvegas1

tvegas1 commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

/azp run UCX PR

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
Comment thread test/gtest/uct/ze/test_ze_copy_md.cc Resolved
Signed-off-by: Yaser Afshar <yaser.afshar@intel.com>
Co-authored-by: Thomas Vegas <125344591+tvegas1@users.noreply.github.com>
@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@svc-nvidia-pr-review

Copy link
Copy Markdown

🤖 Review complete — no issues found.

@yafshar
yafshar requested a review from tvegas1 October 28, 2026 19:58
@svc-ucx

svc-ucx commented Oct 29, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR · commit 6abaccd9

Summary: Unable to fetch logs — cannot diagnose without them.
Root cause: Unknown — log access failed.
Implicated commit: unknown
File: unknown
Suggested fix: Provide a direct link to the failed build log in a follow-up comment.
Log fetch attempts:

  • fetch_azure_logs({'build_url': 'https://dev.azure.com/ucfconsort/0b36e3f0-8ab9-4a48-b68b-[REDACTED:Hex High Entropy String]/_build/results?buildId=141385'}) → [fetch_azure_logs ERROR: no failed job found in timeline for build 141385]
  • fetch_jenkins_stages({'job': 'UCX PR', 'build': 141385}) → [fetch_jenkins_stages ERROR] build URL names Jenkins controller 'https://dev.azure.com/ucfconsort/0b36e3f0-8ab9-4a48-b68b-[REDACTED:Hex High Entropy String]/_build/results?buildId=141385', but this deployment is configured for 'https://nbuprod.blsm.nvidia.com/nb
  • fetch_jenkins_logs({'job': 'UCX PR', 'build': 141385}) → [fetch_jenkins_logs ERROR] build URL names Jenkins controller 'https://dev.azure.com/ucfconsort/0b36e3f0-8ab9-4a48-b68b-[REDACTED:Hex High Entropy String]/_build/results?buildId=141385', but this deployment is configured for 'https://nbuprod.blsm.nvidia.com/nbu-
  • fetch_azure_logs({'build_url': 'https://dev.azure.com/ucfconsort/ucx/_build/results?buildId=141385'}) → [fetch_azure_logs ERROR: no failed job found in timeline for build 141385]
  • fetch_azure_logs({'build_url': 'https://dev.azure.com/ucfconsort/0b36e3f0-8ab9-4a48-b68b-[REDACTED:Hex High Entropy String]/_build/results?buildId=141385&view=logs'}) → [fetch_azure_logs ERROR: no failed job found in timeline for build 141385]

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id 00a0e194-dd9f-433a-86ff-7209deeee604 in the triage console for the audit trail.

@tvegas1

tvegas1 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

/azp run UCX PR

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@tvegas1
tvegas1 merged commit b0180da into openucx:master Oct 30, 2026
167 checks passed
@yafshar
yafshar deleted the fix/ze-copy-memtype-cache-eager branch October 30, 2026 15:02
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.

4 participants