Skip to content

UCT/IB/MLX5: support short dm in purge outstanding - #12058

Merged
evgeny-leksikov merged 14 commits into
openucx:masterfrom
jeynmann:failover_uct_rcx_dm
Oct 16, 2026
Merged

evgeny-leksikov merged 14 commits into
openucx:masterfrom
jeynmann:failover_uct_rcx_dm

Conversation

@jeynmann

@jeynmann jeynmann commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

What?

Add wqe parse support for am/get short dm.

Need rebase after PR#11873 is merged.

Why?

FT need extract info from wqe before replay uct am/get short dm.

How?

Add parser for uct am/get short dm.

@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

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

Comment thread src/uct/ib/rc/base/rc_ep.c Outdated
void uct_rc_ep_short_dm_desc_handler(uct_rc_iface_send_op_t *op,
const void *UCS_V_UNUSED resp)
{
ucs_mpool_put(op);

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.

ucs_mpool_put_inline

evgeny-leksikov
evgeny-leksikov previously approved these changes Oct 9, 2026
@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@svc-ucx

svc-ucx commented Oct 9, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (AddressSanitizer roce on worker 0) · commit 616a94a8

TL;DR: The ASan "roce on worker 0" job did not actually fail a test — it was externally cancelled mid-make install, before any gtest/ASan test ran. This is a CI/infra cancellation (superseded or aborted run), not a code defect in PR #12058; re-run the pipeline.

Full analysis

Summary: Azure Pipelines job "roce on worker 0" (AddressSanitizer) terminated with ##[error]The operation was canceled. during the Run ./contrib/test_jenkins.sh step, while make install was still copying gtest helper libraries.

Root cause: External cancellation, not a test or build failure. Evidence from the log:

  • The final application output is make[2]: Leaving directory '/__w/1/s/build-test/test/gtest/common/googletest' at 11:59:41.7052284Z, immediately followed by ##[error]The operation was canceled. at 11:59:41.9531967Z — a gap of ~0.25 s.
  • Timestamps across the whole visible tail are continuous and dense (11:59:37.8 → 11:59:41.7, hundreds of lines in under 4 seconds, sub-100 ms spacing). There is no stall anywhere — the largest inter-line gap is a fraction of a second. This rules out both a hang and a legitimate wall-clock timeout.
  • There is no DUE TO TIME LIMIT, no Build timed out, no exit code 143/SIGTERM, and critically no ASan report (ERROR: AddressSanitizer, SUMMARY:), no compiler error, and no gtest output at all.
  • The job was still in the install phase of test_jenkins.sh. The ASan gtest suite that this job exists to run had not yet started, so the job never reached code touched by the failover_uct_rcx_dm branch. All libraries (libucs, libuct, libuct_ib, libuct_rdmacm, libucp) compiled, relinked, and installed cleanly with -Werror -fsanitize=address.

The abrupt cancellation at a point of healthy progress, with the pipeline then reporting Skipping step due to condition evaluation and Start cleaning up orphan processes, is the signature of an Azure DevOps run cancellation — typically a newer commit pushed to the PR superseding this run, a manual cancel, or agent reclamation.

Implicated commit: none — commit [REDACTED:Hex High Entropy String] is not implicated; no code from it was exercised.

File: not applicable — failure occurred in CI orchestration, not in a source file. Last log position: contrib/test_jenkins.sh install phase, build-test/test/gtest/common/googletest.

Suggested fix: No code change required. Re-run / re-queue the "AddressSanitizer roce on worker 0" job and let it reach the gtest stage before triaging. Before re-running, confirm whether a newer commit was pushed to failover_uct_rcx_dm around 2026-10-09 11:59:41Z (which would have auto-cancelled this run) or whether someone cancelled the build manually — check the build's "Canceled by" field in the Azure DevOps UI. If this job is cancelled repeatedly at the same install phase without a superseding push, investigate agent health/eviction on the ASan pool rather than the UCX code. Separately, note that test_jenkins.sh performs a full make install with libtool relinking of every module before running tests; this wastes several minutes of the job budget and widens the window for cancellation, so caching or skipping the relink step would make these jobs more robust.

Related: #11932 ("CI: Do not start PR pipelines for draft pull requests") is relevant background on PR-pipeline start/cancel behavior, though not a direct duplicate. No existing issue matches this cancellation signature.

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id 76b10594-c4d2-45c9-b65a-00b05f5636cb in the triage console for the audit trail.

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

Comment thread src/uct/ib/mlx5/rc/rc_mlx5_ep.c Outdated
ucs_assert(desc->super.buffer ==
UCS_PTR_BYTE_OFFSET(iface->dm.dm->start_va,
(uintptr_t)wqe_buffer));
return desc->super.buffer;

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.

mlx5 DM reads require 4-byte accesses. Maybe copy it to host mem w aligned reads before parsing or exposing it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed. Do we have any documentation indicating that the read must be by 4 bytes?
The gtest passed without this change on CX6 and BF3.

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@svc-ucx

svc-ucx commented Oct 12, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (Coverity coverity release on coverity_rh7) · commit ad8e5303

TL;DR: The Coverity release stage failed with 1 new defect — a BAD_FREE at src/uct/ib/mlx5/rc/rc_mlx5_ep.c:1819, introduced by the top commit of this PR (ad8e5303, "UCT/IB/MLX5: copy dm to host buf") which uses the ucs_alloc_on_stack/ucs_free_on_stack macro pair; Coverity cannot correlate the two independent size comparisons, so it believes alloca() memory is passed to ucs_free(). Fix by tracking the heap allocation in an explicit local pointer instead of relying on the macro pair.

Full analysis

Summary: Azure Pipelines job "Coverity release on coverity_rh7" (build 138717) failed at the end of cov-analyze: ##[error]Coverity found 1 issues: /__w/1/s/src/uct/ib/mlx5/rc/rc_mlx5_ep.c:1819 Type: Free of stack-allocated memory (BAD_FREE). The compile and cov-build phases succeeded (559 units, 100%); the gate is purely the defect count (nerrors=1 → exit 1 in buildlib/tools/coverity.sh:112-145).

Root cause: uct_rc_mlx5_ep_outstanding_purge() allocates the DM copy buffer via callback_data.dm_data = ucs_alloc_on_stack(dm_data_len, "rc_purge_dm") (line 1766) and releases it via ucs_free_on_stack(callback_data.dm_data, dm_data_len) (line 1819). Those macros (src/ucs/sys/compiler.h:72-86) each make their own independent size comparison: the alloc macro tests its internal copy _sz <= UCS_ALLOCA_MAX_SIZE, while the free macro tests the caller's dm_data_len > UCS_ALLOCA_MAX_SIZE. Coverity's path explorer treats those as two unrelated branch conditions, and the logs show it doing exactly that — step 13 takes "_sz <= 1200, taking true branch" (i.e. __builtin_alloca), then step 25 takes "dm_data_len > 1200, taking true branch" (i.e. ucs_free). So it constructs an infeasible path where alloca'd memory reaches free() in src/ucs/debug/memtrack.c:371. It's a false positive in the strict sense, but the macro pair is unanalyzable by design and the Coverity gate is blocking.

A secondary real issue on the same lines: ucs_alloc_on_stack can return NULL from ucs_malloc when dm_data_len > UCS_ALLOCA_MAX_SIZE, and the return value is never checked before being handed to uct_rc_mlx5_op_info_fill().

Implicated commit: [REDACTED:Hex High Entropy String] — Zihao Zhao, "UCT/IB/MLX5: copy dm to host buf" (the PR head commit; the ucs_alloc_on_stack/ucs_free_on_stack pair and the dm_data field are new in it)

File: src/uct/ib/mlx5/rc/rc_mlx5_ep.c:1766 (allocation) and src/uct/ib/mlx5/rc/rc_mlx5_ep.c:1819 (reported free); macros at src/ucs/sys/compiler.h:72-86

Suggested fix: Make the stack-vs-heap decision explicit with a single local pointer, so ucs_free() provably only ever receives a malloc'd pointer or NULL. This is an error/purge path, not a fast path, so the branch cost is irrelevant:

    void *dm_data_heap = NULL;   /* add to declarations */
...
#if HAVE_IBV_DM
    if (iface->dm.dm != NULL) {
        dm_data_len = ucs_align_up(iface->dm.seg_len, sizeof(uint64_t));
        if (dm_data_len > UCS_ALLOCA_MAX_SIZE) {
            dm_data_heap = ucs_malloc(dm_data_len, "rc_purge_dm");
            if (dm_data_heap == NULL) {
                return UCS_ERR_NO_MEMORY;
            }
            callback_data.dm_data = dm_data_heap;
        } else {
            callback_data.dm_data = ucs_alloca(dm_data_len);
        }
    } else
#endif
    { dm_data_len = 0; callback_data.dm_data = NULL; }
...
    ucs_free(dm_data_heap);      /* replaces ucs_free_on_stack() at line 1819 */

Given iface->dm.seg_len is a configured device-memory segment length that can plausibly exceed UCS_ALLOCA_MAX_SIZE (1200), simply always using ucs_malloc/ucs_free here is also acceptable and even simpler. Do not paper over this with a Coverity suppression — the unchecked NULL return makes it worth restructuring regardless. Longer term, consider reworking ucs_alloc_on_stack/ucs_free_on_stack in src/ucs/sys/compiler.h to return/accept an explicit "needs free" token, since any future caller will hit the same BAD_FREE report.

Related: PR #12058 (this change); prior commits in the same series: 757f9f9b (#11866), 2dbbd8c4 (#12006), f9426106 (#12019), 7f839dd3 (#11874). The macro pair originates in [REDACTED:Hex High Entropy String] (Thomas Vegas, UCS/ASYNC batch timers). No existing issue found for this Coverity signature.

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id 0beadef3-ef0c-49e1-bb20-e0509ea046be in the triage console for the audit trail.

@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

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

@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

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

@svc-ucx

svc-ucx commented Oct 13, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (Tests gpu on worker 1) · commit 1d151d08

TL;DR: The GPU gtest job failed on a single UCP protocol-selection test, shm_ib/test_ucp_proto_am_rndv.am_explicit_scheme_disables_force_provenance/0, which hard-asserts that am/rndv is selected for 1 MB CUDA buffers even with RNDV_SCHEME=get_zcopy; this is an environment-dependent test assumption unrelated to PR #12058 (a UCT/IB/MLX5 DM purge change). Fix the test gating (require_cuda_net_md() / skip when am/rndv isn't selectable) rather than the PR.

Full analysis

Summary: make test in the gtest stage returned error 1 because exactly 1 of 10432 tests failed: shm_ib/test_ucp_proto_am_rndv.am_explicit_scheme_disables_force_provenance/0, where GetParam() = shm,ib,cuda_copy,rocm_copy (log line 2026-10-13T10:11:43.7251361Z, followed by make: *** [Makefile:4926: test] Error 1).

Root cause: The test runs with RNDV_PIPELINE_SHM_CUDA_STAGING_FORCE=y + RNDV_SCHEME=get_zcopy and calls check_am_rndv_remote_proto_config(0, 0), which goes through select_am_rndv_remote_proto_config(). That helper does a hard EXPECT_STREQ("am/rndv", thresh->proto_config.proto->name) and then ASSERT_NE(nullptr, remote_proto_config) for a 1 MB CUDA UCP_OP_ID_AM_SEND selection. Because RNDV_SCHEME=get_zcopy forces every rendezvous variant other than get/zcopy to report UCS_MEMUNITS_INF (src/ucp/rndv/proto_rndv.c:505-518, ucp_proto_rndv_cfg_thresh() in src/ucp/rndv/proto_rndv.inl:140-149), am/rndv only survives at 1 MB if a get_zcopy-capable MD can actually register CUDA memory on this worker. The test only guards with require_cuda_rndv_support() (which merely checks CUDA presence + is_rndv_supported()), unlike the sibling CUDA-staging tests that correctly use require_cuda_net_md() (test/gtest/ucp/test_ucp_proto.cc:98-112, used at lines 656 and 717). On this shm,ib,cuda_copy,rocm_copy GPU worker the get_zcopy path for CUDA is not modelled, an eager AM protocol wins the 1 MB threshold, and the EXPECT_STREQ/ASSERT_NE fire.

Note: the Azure API only returns the tail of the job log, so the exact EXPECT_* line text was not recoverable — the identification above comes from the failed-test name plus the test source. Nothing in PR #12058 (UCT/IB/MLX5: support short dm in purge outstanding) touches UCP protocol selection, rkey configs, or rendezvous thresholds, so this is not a regression from the PR; the three sibling tests (am_default_remote_model_preserves_legacy_key, am_force_remote_model_remains_untagged, rma_explicit_scheme_disables_force_provenance) all passed, isolating the failure to the explicit-scheme + AM combination.

Implicated commit: Not the PR under test. The test and its gating come from the CUDA staging-force series by Nathan Bellalou — b34ac9a1 "UCP/RNDV: Add opt-in intra-node CUDA staging policy", 9cc08081 "UCP/RNDV: use tag-only signal for CUDA staging force", and 8a76f891 "GTEST: Add CUDA/RNDV guards for tests" (merged via d9c844ae, PR #11641).

File: test/gtest/ucp/test_ucp_proto.cc:829-837 (test body) and test/gtest/ucp/test_ucp_proto.cc:439-446 (the unconditional EXPECT_STREQ("am/rndv", ...) in select_am_rndv_remote_proto_config)

Suggested fix:

  1. Strengthen the guard in am_explicit_scheme_disables_force_provenance (and the am_*/rma_* explicit-scheme siblings) from require_cuda_rndv_support() to require_cuda_net_md(), matching rndv_force_cuda_pipeline_selects_attached_path at line 656 — without a network MD that can register CUDA memory, am/rndv cannot be selected under RNDV_SCHEME=get_zcopy.
  2. Make select_am_rndv_remote_proto_config() tolerant: replace the hard EXPECT_STREQ("am/rndv", ...) with UCS_TEST_SKIP_R("am/rndv protocol is not selected") when the chosen protocol differs, mirroring the "stub protocol" skip already used in rndv_force_cuda_send_child_uses_tag_provenance (line 724). The test's actual intent — that no UCP_RKEY_CONFIG_FLAG_PROTO_ESTIMATION rkey config is created when an explicit RNDV_SCHEME disables the force policy — can still be asserted via count_rkey_configs_with_flag() independently of which protocol wins the 1 MB threshold.
  3. Re-run the PR UCT/IB/MLX5: support short dm in purge outstanding #12058 job; this test should not be treated as a blocker for that change.

Related: PR #12058 (the build under test, unrelated), PR #11641 / commits b34ac9a1, 9cc08081, 8a76f891, d9c844ae (origin of the test), PR #12041 "UCP/RNDV: Use local memory detection for RTR_REQ responder" and PR #11902 "UCP/PROTO: Fix buffer copy estimation for CPU-accessible memory" (both touch the same CUDA staging/provenance selection area).

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@gleon99
gleon99 enabled auto-merge (squash) October 14, 2026 13:37
@gleon99

gleon99 commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

@jeynmann please follow up on CI, merge.

auto-merge was automatically disabled October 14, 2026 15:04

Head branch was pushed to by a user without write access

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@evgeny-leksikov
evgeny-leksikov enabled auto-merge (squash) October 14, 2026 15:22
@svc-ucx

svc-ucx commented Oct 14, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (Tests roce on worker 1) · commit 098fbd63

TL;DR: rc_verbs/test_uct_perf.envelope/0 hung for exactly 900 s right after the am short iov rate64 sub-test, i.e. while starting the first bcopy AM perf test, and the gtest watchdog aborted the run; this is a hang in the RC TX send-op/completion bookkeeping introduced by the branch's DM / outstanding-purge refactor, not a slow test — do not raise the timeout.

Full analysis

Summary: Azure job "roce on worker 1" failed with make: *** [Makefile:4925: test] Aborted (core dumped) because the gtest watchdog killed rc_verbs/test_uct_perf.envelope/0 after 15 minutes of no progress.

Root cause: Timestamp analysis of the stage log shows continuous activity up to 2026-10-14T15:47:47.60 … mlx5_0:1 am short iov rate64 : 4.991 Mpps, then a single ~15-minute gap (15:47:47 → 16:02:45) with zero output, ending in:

test/gtest/common/test_helpers.cc:57: Failure
Failed
Connection timed out - abort testing
Caught signal 6 (Aborted)

That is the gtest watchdog (watchdog_timeout = 900. in test/gtest/common/test_helpers.cc:31, ADD_FAILURE() << strerror(ETIMEDOUT) << " - abort testing" at line 57) firing, so the gap is a hang, not slowness. The backtrace confirms it: the main thread is parked in pthread_join at test/gtest/common/test_perf.cc:300 (test_perf::run_multi_threaded) — one of the two perf worker threads never returned, i.e. it was spinning in ucx_perf_run/rte_comm::pop waiting for a UCT completion that never arrived.

The operation that hung is identifiable from the spec table order in test/gtest/uct/test_uct_perf.cc: the last completed entry is am short iov rate64 (line 120) and the next entry is am bcopy latency (line 127, UCX_PERF_CMD_AM / UCT_PERF_DATA_LAYOUT_BCOPY) — the first descriptor-based (bcopy) send in the sequence. All short/inline variants passed; the first op that allocates a TX descriptor and requires a send-op completion deadlocked.

This points squarely at the branch under test. failover_uct_rcx_dm rewrites exactly that bookkeeping in code shared by rc_verbs (not just mlx5):

  • src/uct/ib/rc/base/rc_ep.h:424 — uct_rc_txqp_add_send_comp_always(): "Always create a send op, even if comp is NULL, so that outstanding purge can identify the operation by its completion handler." It pulls from iface->tx.free_ops via uct_rc_iface_get_send_op() (rc_iface.h:523), which has no NULL check and is backed by a fixed ops_buffer of only tx_cq_len entries.
  • src/uct/ib/rc/base/rc_ep.c:343 — new uct_rc_ep_short_dm_desc_handler(), plus new handler-identity dispatch in uct_rc_txqp_purge_outstanding() (rc_ep.c:500-559) and the handler whitelist at rc_ep.c:538-554.

If an op created on the comp == NULL path isn't matched by one of those handler-identity branches, it is never returned to free_ops; the pool drains, uct_rc_ep_has_tx_resources()/uct_rc_ep_have_tx_cqe_avail() stay false forever, the perf thread's pending op never completes, and pthread_join blocks until the watchdog fires — exactly the observed signature.

Implicated commit: [REDACTED:Hex High Entropy String] (Zihao Zhao, merge of master into failover_uct_rcx_dm) carrying the branch's RC send-op/DM series: 00b6a19d "UCT/IB/MLX5: support dm", 616a94a8 "put dm desc inline", 0f599995 "support am zcopy in purge outstanding", 1d151d08 "cleanup" (all Zihao Zhao). Base-layer regression risk is in rc_ep.c/rc_ep.h, which rc_verbs shares with mlx5.

File: src/uct/ib/rc/base/rc_ep.h:424 (uct_rc_txqp_add_send_comp_always) and src/uct/ib/rc/base/rc_ep.c:343, :500-559 (new DM handler + purge/complete handler dispatch); hang observed from test/gtest/uct/test_uct_perf.cc:127 (am bcopy latency), watchdog at test/gtest/common/test_helpers.cc:57.

Suggested fix:

  1. Reproduce locally and narrow it: UCX_TLS=rc_verbs ucx_perftest -t am_lat -d mlx5_0:1 -x rc_verbs with a bcopy data layout, or run just --gtest_filter='rc_verbs/test_uct_perf.envelope*' under gdb/UCX_LOG_LEVEL=trace_poll and dump iface->tx.free_ops, iface->tx.cq_available, ep->txqp.available and the txqp.outstanding queue at the hang.
  2. Audit every new uct_rc_txqp_add_send_comp_always() / unconditional send-op creation call site added by the DM series and confirm each resulting op's handler appears in both the completion path and the uct_rc_txqp_purge_outstanding() release branches (rc_ep.c:538-554) so it is always returned via uct_rc_iface_put_send_op(). The new uct_rc_ep_short_dm_desc_handler only falls into the ucs_mpool_put_inline path — verify ops that are iface-buffer backed are not routed there (and vice versa).
  3. Harden uct_rc_iface_get_send_op() (rc_iface.h:523) with an assert/NULL check so pool exhaustion fails loudly instead of corrupting the free list, and consider making uct_rc_iface_tx_ops_cleanup()'s "send ops were not released" warning fatal in debug builds — that would have caught this leak at iface teardown instead of as a 15-minute CI hang.
  4. Do not bump watchdog_timeout or the Azure job time limit; the log shows zero activity during the gap, so the work was not making progress.

Related: Prior hangs with the same test signature: #4975 (dc_mlx5/test_uct_perf.envelope/0 hang), #3866. Branch series PRs: #11873, #11866, #11836, #11668 (outstanding purge implementation), #12033.

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id 14742c23-4cde-4e2d-b66b-10fd3fc8f539 in the triage console for the audit trail.

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@svc-ucx

svc-ucx commented Oct 15, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (AddressSanitizer roce on worker 1) · commit a45e7c8a

TL;DR: The ASAN gtest run was aborted by the 900 s gtest watchdog (SIGTERM → signal 6) because rc_mlx5/test_uct_peer_failure_multiple.test/1 <rc_mlx5/mlx5_1:1> hung — the AM-short connectivity check done for every new receiver (test_peer_failure.cc:188) never saw its AM (m_am_count[index] stayed 0), timing out 4× in a row at ~200 s each. This is a real AM-delivery/completion hang on the RC mlx5 path of this branch, not a job that merely needed more wall time.

Full analysis

Summary: make test aborted (Makefile:4925: test] Aborted (core dumped), Bash exit 2) after the gtest watchdog fired during rc_mlx5/test_uct_peer_failure_multiple.test/1.

Root cause: Log timeline shows the problem clearly:

  • 09:32:57 — [ RUN ] rc_mlx5/test_uct_peer_failure_multiple.test/1 <rc_mlx5/mlx5_1:1> is the last meaningful application output.
  • Then four ~3 m 20 s (≈200 s) silent gaps, each ending in the same failure at test/gtest/uct/test_peer_failure.cc:188: m_am_count[index] = 0, expected 1.
  • 09:47:57 (exactly 900 s = watchdog_timeout in test/gtest/common/test_helpers.cc:31 after test start) — test_helpers.cc:57: Failure / Connection timed out - abort testing, watchdog sends SIGTERM, backtrace ends in libuct_ib_mlx5.so, process aborts.

Line 188 is inside test_uct_peer_failure::send_recv_am(), which on this path is only reached from new_receiver() (test_peer_failure.cc:146) — the "make sure that TL is up and has resources" probe run for each of the m_nreceivers receivers created in test_uct_peer_failure_multiple::init() (m_nreceivers = tx_queue_len, i.e. up to 256 ifaces). So the AM short posted on a freshly connected rc_mlx5 endpoint was never delivered/completed; each probe burned the full wait_for_flag deadline (10 s × ASAN time multiplier ≈ 200 s), and four of those exhausted the 900 s watchdog. The gaps are >2 minutes of total silence, so this is a hang, not slow-but-progressing work.

Suspect code is this branch's RC/mlx5 device-memory + outstanding-purge rework, which rewrites the short send and send-op bookkeeping used by exactly this path: uct_rc_mlx5_base_ep_am_short() / uct_rc_mlx5_common_ep_short_dm() and the credit/PSN accounting in uct_rc_mlx5_ep_save_ft_ci() / uct_rc_mlx5_ep_outstanding_purge() / uct_rc_mlx5_iface_update_tx_cq_res(). A secondary aggravating factor visible in the test itself: init() sets RC_TIMEOUT?=100us and RC_RETRY_COUNT?=4 (test_peer_failure.cc:34-35) un-scaled by ucs::test_time_multiplier(), which is extremely aggressive under AddressSanitizer with hundreds of ifaces and can silently fail the probe endpoint.

Implicated commit: branch head [REDACTED:Hex High Entropy String] (PR #12058, Zihao Zhao); most relevant content commits on failover_uct_rcx_dm: 00b6a19d "UCT/IB/MLX5: support dm", 616a94a8 "UCT/IB/MLX5: put dm desc inline", ad8e5303 "UCT/IB/MLX5: copy dm to host buf" (all Zihao Zhao), plus merge [REDACTED:Hex High Entropy String] which pulled in 8ec39383 "Save the transmit range inside outstanding purge (#12033)" (Evgeny Leksikov).

File: test/gtest/uct/test_peer_failure.cc:188 (failing assertion) / test/gtest/uct/test_peer_failure.cc:141-147 (the hanging probe) — underlying suspect src/uct/ib/mlx5/rc/rc_mlx5_ep.c:441-478 (uct_rc_mlx5_base_ep_am_short) and src/uct/ib/mlx5/rc/rc_mlx5_ep.c:1829-1852 (uct_rc_mlx5_ep_save_ft_ci / uct_rc_mlx5_ep_purge_finish_tx).

Suggested fix:

  1. Reproduce locally with --gtest_filter='rc_mlx5/test_uct_peer_failure_multiple.test/1' and UCX_LOG_LEVEL=debug on a RoCE device, and check whether err_cb fires (ep marked failed) while the probe waits. If it does, the AM is being lost to an endpoint error, not to a missing CQE.
  2. Audit the branch's short/DM send bookkeeping for a lost completion: verify txwq->sig_pi/prev_sw_pi and txwq->ft_ci updates in uct_rc_mlx5_ep_save_ft_ci() and uct_rc_mlx5_ep_purge_finish_tx() cannot leave a freshly created ep without TX credits, and that uct_rc_mlx5_common_ep_short_dm() always arms MLX5_WQE_CTRL_CQ_UPDATE for the dm desc it enqueues (uct_rc_ep_short_dm_desc_handler).
  3. Independently harden the test so a single stuck probe does not eat the whole 900 s watchdog: scale RC_TIMEOUT/RC_RETRY_COUNT by ucs::test_time_multiplier() in test_uct_peer_failure::init() and cap m_nreceivers under sanitizer builds (the same way it is already capped for aarch64/AWS at test_peer_failure.cc:1193-1200). Do not raise the Azure job time limit — the run was idle, not busy.

Related: PR #12058 (this change), #12033 (transmit range in outstanding purge), #11873, #11866, #11836 (earlier purge-outstanding series on the same RC mlx5 path).

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id fe82d87a-661f-4825-bd4e-ec65aba5aa73 in the triage console for the audit trail.

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@svc-ucx

svc-ucx commented Oct 15, 2026

Copy link
Copy Markdown

🤖 CI Triage Agent — UCX PR (Tests roce on worker 1) · commit 58787d61

TL;DR: The job was killed after a ~58-minute hang (not a slow test): uct_hello_world on rc_verbs/mlx5_1:1 never produced a single line of output because the mlx5_1 HCA on swx-rain04 is wedged — its UMR QP operations time out (30 s) and ibv_post_send(UMR QP, invalidation) fails. This is an environment/node failure plus a missing timeout guard in the CI harness, not a defect in PR #12058.

Full analysis

Summary: "Tests roce on worker 1" was cancelled by the Azure agent shutdown after uct_hello_world -d mlx5_1:1 -t rc_verbs hung indefinitely; orphan processes 1981460/1983257 (uct_hello_world) had to be terminated at job finalize.

Root cause: The mlx5_1 device on swx-rain04 is in a broken state and three independent pieces of log evidence converge on it:

  1. ib_mlx5dv_md.c:1449 ERROR mlx5_1: ibv_poll_cq(UMR CQ, registration, UMR mkey ... index 0x4c6) timed out — the 30 s guard in uct_ib_mlx5_devx_umr_post_sync() expired.
  2. Immediately after: ib_mlx5dv_md.c:1440 ERROR mlx5_1: ibv_post_send(UMR QP, invalidation, ...) returned 12: Numerical result out of range, then <Failed to pack rkey: Input/output error>. The UCP context reports create time: 30091.203 ms — exactly the UMR timeout, confirming the whole context open stalled on mlx5_1.
  3. get_rdma_device_ip_addr mlx5_1:1 returned empty → Cannot find IPv4 address for device mlx5_1:1, so ibdev2netdev does not report the device's netdev as Up.

Timeline proves a hang, not slowness: the largest inter-line gap is 12:38:55.209 (last meaningful line: taskset -c 11 ./examples/uct_hello_world -d mlx5_1:1 -t rc_verbs -i -m host -n swx-rain04 -p 16504) → 13:36:34.188 (agent shutdown) — ~58 minutes of complete silence, essentially the entire remaining wall time. Notably the mlx5_1 server instance printed nothing at all (no Using rc_verbs/mlx5_1:1, no Waiting for connection..., unlike the successful mlx5_0:1 run), so it hung inside device/MD initialization on the bad HCA rather than in the data path.

Two contributing CI-harness gaps amplified the node fault into a full job loss:

  • get_ib_devices() in buildlib/tools/common.sh:176-194 selects a device purely on PORT_ACTIVE + presence of gids/0, while get_rdma_device_ip_addr() (line 205) additionally requires ibdev2netdev | grep Up. run_ucx_perftest_with_daemon therefore correctly skipped mlx5_1:1, but run_uct_hello iterates get_active_ib_devices() and still ran on it.
  • run_client_server_app launches server/client with a bare sleep 15 + wait and no timeout wrapper, so a stuck process blocks until the agent kills the job.

Implicated commit: unknown — no UCX commit is implicated. PR #12058 ("UCT/IB/MLX5: support short dm in purge outstanding") touches the RC/MLX5 device-memory purge path, which is not exercised by rc_verbs uct_ep_am_short hello-world and is unrelated to UMR mkey registration. The last change to src/uct/ib/mlx5/dv/ib_mlx5dv_md.c is [REDACTED:Hex High Entropy String] (Thomas Vegas, 2026-09-14), well before this build.

File: src/uct/ib/mlx5/dv/ib_mlx5dv_md.c:1447 (UMR poll timeout that fired for mlx5_1); CI gaps at buildlib/tools/common.sh:176-194 and run_client_server_app in contrib/test_jenkins.sh

Suggested fix:

  1. Immediate: Re-run the PR — this is not a code failure. Flag swx-rain04 for maintenance: mlx5_1 reports PORT_ACTIVE with a valid GID but has no Up netdev and a non-functional UMR QP; reset the device/firmware or drop the node from the roce pool.
  2. Fail fast instead of burning wall clock: wrap the server/client invocations in run_client_server_app with timeout --kill-after=10 <N> so a wedged HCA produces a clear test failure in seconds rather than a 58-minute agent cancellation with orphan processes.
  3. Make device selection consistent: for RoCE jobs, filter get_active_ib_devices() through the same ibdev2netdev | grep Up / netdev-has-IPv4 check used by get_rdma_device_ip_addr(), so devices without a usable RoCE netdev are skipped by run_uct_hello too — the same way the perftest-daemon test already skipped mlx5_1:1.
  4. Harden the UMR path (robustness, separate change): when uct_ib_mlx5_devx_umr_post_sync() returns UCS_ERR_TIMED_OUT, the UMR QP is left in an unusable state, so the follow-up IBV_WR_LOCAL_INV post predictably fails. Mark the UMR context as failed on timeout and skip further posts, and consider making the hard-coded 30 s timeout configurable so a dead device surfaces as an error quickly instead of stalling context creation.

Related: PR #12058 (#12058) — the PR under test, not the cause. No existing issue tracks the mlx5_1 UMR CQ timeout on this node; worth opening one for the UMR-timeout-then-failed-invalidation cascade.

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id 68ae5f66-a3b7-438a-ab0f-3cdf44e28155 in the triage console for the audit trail.

@svc-nvidia-pr-review

Copy link
Copy Markdown

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

@evgeny-leksikov
evgeny-leksikov merged commit c20a738 into openucx:master Oct 16, 2026
163 of 165 checks passed
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.

6 participants