UCT/ROCM: Enable dmabuf by default with peermem fallback - #11934
UCT/ROCM: Enable dmabuf by default with peermem fallback#11934ddebonis-amd wants to merge 1 commit into
Conversation
|
🤖 Starting review — findings will be posted here when done. |
|
🤖 CI Triage Agent — TL;DR: This is not a code/test bug — the "AUTHORS file update check" failed because PR #11934's commit author, David DeBonis ddebonis@amd.com, is a new contributor who is not listed in the Full analysisSummary: The Root cause: The pipeline step computes the PR commit range ( Since neither the email Implicated commit: [REDACTED:Hex High Entropy String] (David DeBonis ddebonis@amd.com) — the PR head commit, which introduces a new author without an File: Suggested fix: In PR #11934, add the author to # from the repo root, on the PR branch
./contrib/authors_update.sh $(git merge-base HEAD origin/develop)..HEAD
git add AUTHORS && git commit -m "AUTHORS: add David DeBonis"Or simply insert Related: PR #11934 (the failing PR). No pre-existing issue tracks this failure mode; recent
|
|
Residual coverage gap: the ROCm CI containers ( |
|
🤖 Starting review — findings will be posted here when done. |
|
🤖 CI Triage Agent — TL;DR: The "commit title" codestyle job failed because one commit in PR #11934 is titled Full analysisSummary: Azure Pipelines job Root cause: Not a product or infrastructure bug. The log shows:
Secondary (cosmetic) CI issue visible in the same log: the step runs with Implicated commit: [REDACTED:Hex High Entropy String] (PR #11934 head, merged as 63e9a2a for CI) — the offending commit is the one titled File: buildlib/tools/codestyle.sh:14 (the enforcing regex); buildlib/pr/codestyle.yml:17-30 (the failing step) Suggested fix:
Related: PR #11934; contributor guidance: https://github.com/openucx/ucx/wiki/Guidance-for-contributors#general-guidelines
|
|
🤖 CI Triage Agent — TL;DR: The "commit title" codestyle job failed because two commits in PR #11934 have titles that don't match UCX's required Full analysisSummary: Azure job Root cause: Not an infrastructure or product bug.
Both bad titles set Implicated commit: [REDACTED:Hex High Entropy String] (PR #11934 head, merged as 2f6f623 for testing) — the offending commits are authored by the PR submitter, not by a UCX maintainer change. File: buildlib/tools/codestyle.sh:14 (check logic) — enforced via buildlib/pr/codestyle.yml:24 Suggested fix: Rewrite the PR history so every commit title has the required form. Simplest is to squash the three commits into one: If the commits must stay separate, amend each title to e.g. Related: PR #11934; contributor guidance referenced by the job: https://github.com/openucx/ucx/wiki/Guidance-for-contributors#general-guidelines
|
|
Residual gaps: both |
|
🤖 Starting review — findings will be posted here when done. |
|
Residual coverage gap: the ROCm CI jobs are build-only, so neither the compile break nor the offset math would be caught by the PR pipeline; an AMD GPU run of |
|
🤖 Starting review — findings will be posted here when done. |
|
🤖 CI Triage Agent — TL;DR: This is not an infrastructure failure — the "commit title" codestyle job correctly rejected PR #11934 because two of its commits have titles that lack the required Full analysisSummary: Azure Pipelines job Root cause: The job runs
Two bad titles set Implicated commit: No product regression. The offending commits are the intermediate commits of PR #11934 (merge commit File: Suggested fix:
Related: PR #11934; guidelines referenced by the job: https://github.com/openucx/ucx/wiki/Guidance-for-contributors#general-guidelines
|
|
🤖 Starting review — findings will be posted here when done. |
|
Residual coverage gap: ROCm CI jobs are build-only, so neither Residual coverage gap: Residual coverage gap: nothing covers a failing |
|
🤖 Starting review — findings will be posted here when done. |
|
|
||
| #include <ucs/sys/string.h> | ||
| #include <ucs/sys/module.h> | ||
| #include <ucs/sys/ptr_arith.h> |
There was a problem hiding this comment.
minor: ptr_arith.h isn't needed here, UCS_PTR_BYTE_DIFF comes from ucs/sys/compiler_def.h (ze_copy_md.c uses it for the same dmabuf offset math without this include).
| UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_OFFSET); | ||
| } | ||
|
|
||
| status = uct_rocm_base_mem_query(uct_md, addr, length, |
There was a problem hiding this comment.
Redundant state in the new wrapper: can we pass md->have_dmabuf down to uct_rocm_base_mem_query() instead of clearing and restoring the caller's field_mask? It has only this one caller, so that would drop both the mask juggling and the fd/offset fixup below.
|
Residual gap: |
|
🤖 Starting review — findings will be posted here when done. |
| (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_OFFSET)) { | ||
| uct_rocm_base_dmabuf_export(export_addr, export_len, mem_type, | ||
| &dmabuf_fd, &dmabuf_offset); | ||
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { |
There was a problem hiding this comment.
blocker: duplicated if (DMABUF_FD) block — returns a closed fd and leaks the fd.
if (mem_attr_p->field_mask & ..._DMABUF_FD) {
mem_attr_p->dmabuf_fd = dmabuf_fd;
if (mem_attr_p->field_mask & ..._DMABUF_FD) {
mem_attr_p->dmabuf_fd = dmabuf_fd;
} else {
ucs_close_fd(&dmabuf_fd);
}
ucs_close_fd(&dmabuf_fd);
}Brace matching makes this compile but it is semantically wrong in both directions:
- When
DMABUF_FDis requested, the fd is stored intomem_attr_p->dmabuf_fdand then unconditionally closed by the trailingucs_close_fd(). The caller (ucp_memh_register_internal()→ibv_reg_dmabuf_mr(), anducp_memh_register_internal()'s ownucs_close_fd(®_params.dmabuf_fd)) receives a stale fd number: registration fails withEBADF, or worse, the number is reused by another thread and a wrong file gets registered / double-closed. - When only
DMABUF_OFFSETis requested, the whole block is skipped, so the exported fd is never closed — fd leak on every offset-only query (gtestcheck_fd_leaks()will also flag it).
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| mem_attr_p->dmabuf_fd = dmabuf_fd; | |
| } else { | |
| ucs_close_fd(&dmabuf_fd); | |
| } |
| } | ||
| } | ||
|
|
||
| UCS_TEST_P(test_md_dmabuf, rocm_dmabuf_disable, "ROCM_COPY_DMABUF~=no") |
There was a problem hiding this comment.
minor (test placement/coverage), both non-blocking:
- The test asks for
UCT_MD_MEM_ATTR_FIELD_DMABUF_OFFSETbut never checks it;EXPECT_EQ(0, mem_attr.dmabuf_offset)would also cover the disabled path of the new offset math. - It lives in the generic
test_md_dmabuffixture and relies onROCM_COPY_DMABUF~=noto skip all other MDs. Existing precedent for a component-specific test is a dedicated fixture plus_UCT_MD_INSTANTIATE_TEST_CASE(test_rocm, rocm_cpy)(seetest_gdr_copy), which makes the intent explicit instead of depending on the config-skip trick.
|
Residual gaps: the two mangled hunks mean the current head almost certainly does not build with ROCm enabled; ROCm CI jobs are build-only and not part of the default pipeline, so neither the compile break nor the fd lifetime bug would be caught. An AMD GPU run of Residual gap: nothing exercises a failing |
|
🤖 Starting review — findings will be posted here when done. |
| UCS_CONFIG_TYPE_TABLE(ucs_config_rcache_table)}, | ||
|
|
||
| {"DMABUF", "no", | ||
| {"DMABUF", "try", |
There was a problem hiding this comment.
With try as default, md_attr.dmabuf_mem_types now advertises ROCm dmabuf based only on the kernel-config/kallsyms heuristic in uct_rocm_base_is_dmabuf_supported(). UCP then adds every dmabuf-capable MD to reg_md_map[ROCM] (ucp_context.c, "we can register memory of this type with any md that supports dmabuf"), so ROCm buffers are pushed to IB MDs even when hsa_amd_portable_export_dmabuf() fails at runtime and peermem is not loaded — that turns "not registered on IB" into a registration error. cuda_copy handles this by clearing UCS_MEM_FLAG_REGISTRABLE when the export fails (uct_cuda_copy_md_is_registrable()), while uct_rocm_base_mem_query() always reports UCS_MEM_FLAG_REGISTRABLE in ucs_memtype_cache_update(). Can we detect the export failure the same way, or is the peermem fallback guaranteed to cover it?
| ASSERT_UCS_OK(status); | ||
| EXPECT_EQ(alloc_mem_type, mem_attr.mem_type); | ||
| if ((alloc_mem_type == UCS_MEMORY_TYPE_CUDA) || | ||
| (alloc_mem_type == UCS_MEMORY_TYPE_ROCM) || |
There was a problem hiding this comment.
Minor: the outer condition now lists UCS_MEMORY_TYPE_ROCM and then re-tests it inside; pls give ROCm its own branch so the expectations read directly.
|
Residual coverage gap: ROCm CI is build-only, so neither the compile break nor the offset math is exercised by the PR pipeline; an AMD GPU run of |
|
🤖 Starting review — findings will be posted here when done. |
| length); | ||
| ucs_debug("failed to export dmabuf handle for addr %p / %zu: 0x%x", | ||
| addr, length, status); | ||
| ucs_debug("failed to export dmabuf handle for addr %p / %zu: 0x%x", |
There was a problem hiding this comment.
the log line is duplicated
| ucs_debug("failed to export dmabuf handle for addr %p / %zu: 0x%x", | |
| ucs_debug("failed to export dmabuf handle for addr %p / %zu: 0x%x", | |
| addr, length, status); |
| mem_attr_p->dmabuf_fd = dmabuf_fd; | ||
| is_exported = 1; | ||
| } | ||
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { |
There was a problem hiding this comment.
minor: ucs_close_fd() already ignores UCT_DMABUF_FD_INVALID, so the extra check can go
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| mem_attr_p->dmabuf_fd = dmabuf_fd; | |
| } else { | |
| ucs_close_fd(&dmabuf_fd); | |
| } |
| return UCS_OK; | ||
| } | ||
|
|
||
| static ucs_status_t |
There was a problem hiding this comment.
can we pass md->have_dmabuf down to uct_rocm_base_mem_query() instead of clearing and restoring mem_attr_p->field_mask? it avoids temporarily modifying the caller's input struct, and keeps the dmabuf decision in one place like cuda_copy does.
| } | ||
| } | ||
|
|
||
| UCS_TEST_P(test_md_dmabuf, rocm_dmabuf_disable, "ROCM_COPY_DMABUF~=no") |
There was a problem hiding this comment.
minor: can we put this in a rocm-only fixture with _UCT_MD_INSTANTIATE_TEST_CASE(test_rocm, rocm_cpy) (like test_gdr_copy) instead of relying on the ~= modifier to skip it for every other MD?
| @@ -542,10 +542,16 @@ UCS_TEST_P(test_md, mem_type_detect_mds) { | |||
| ASSERT_UCS_OK(status); | |||
| EXPECT_EQ(alloc_mem_type, mem_attr.mem_type); | |||
| if ((alloc_mem_type == UCS_MEMORY_TYPE_CUDA) || | |||
There was a problem hiding this comment.
minor: ROCM is tested twice in nested conditions; a separate if (alloc_mem_type == UCS_MEMORY_TYPE_ROCM) branch before the CUDA/ZE one would read better.
|
Residual gap: ROCm CI jobs are build-only, so neither the offset math ( Residual gap: |
|
🤖 CI Triage Agent — TL;DR: The Full analysisSummary: Root cause: Syntax error from duplicated code, not an environment/timing issue. In 494: if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) {
495: mem_attr_p->dmabuf_fd = dmabuf_fd;
496: } else {
497: ucs_close_fd(&dmabuf_fd);
498: }
499: ucs_close_fd(&dmabuf_fd); <-- stray duplicate
500: } <-- stray extra closing braceThe extra Implicated commit: [REDACTED:Hex High Entropy String] — "UCT/ROCM: Enable dmabuf by default with peermem fallback", David DeBonis (PR #11934) File: src/uct/rocm/base/rocm_base.c:499-500 Suggested fix: Delete the two duplicated lines so the block reads: if ((mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) ||
(mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_OFFSET)) {
uct_rocm_base_dmabuf_export(base_addr, base_size, mem_type, &dmabuf_fd,
&dmabuf_offset);
if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) {
mem_attr_p->dmabuf_fd = dmabuf_fd;
} else {
ucs_close_fd(&dmabuf_fd);
}
if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_OFFSET) {
mem_attr_p->dmabuf_offset = dmabuf_offset +
UCS_PTR_BYTE_DIFF(base_addr, addr);
}
}Then re-push; a local Related: PR #11934 (the PR under test); prior ROCm dmabuf work in #11541.
|
| mem_attr_p->dmabuf_fd = dmabuf_fd; | ||
| is_exported = 1; | ||
| } | ||
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { |
There was a problem hiding this comment.
blocker: braces are unbalanced here (extra } after the offset block) and ucs_close_fd() is duplicated, so this does not compile and the fd would be closed twice. Additionally, the DMABUF_OFFSET block ends up outside the if that produced dmabuf_offset.
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_OFFSET) { | |
| mem_attr_p->dmabuf_offset = dmabuf_offset + | |
| UCS_PTR_BYTE_DIFF(base_addr, addr); | |
| } | |
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| mem_attr_p->dmabuf_fd = dmabuf_fd; | |
| } else { | |
| ucs_close_fd(&dmabuf_fd); | |
| } | |
| } |
Also, if ((mask & FD) || (mask & OFFSET)) can be if (mask & (FD | OFFSET)).
| ASSERT_UCS_OK(uct_md_mem_query(md(), mem_buf.ptr(), mem_buf.size(), | ||
| &mem_attr)); | ||
| EXPECT_EQ(UCT_DMABUF_FD_INVALID, mem_attr.dmabuf_fd); | ||
| EXPECT_EQ(0ul, mem_attr.dmabuf_offset); |
There was a problem hiding this comment.
minor: duplicated line.
| EXPECT_EQ(0ul, mem_attr.dmabuf_offset); | |
| EXPECT_EQ(0ul, mem_attr.dmabuf_offset); | |
| } |
| } | ||
| } | ||
|
|
||
| UCS_TEST_P(test_md_dmabuf, rocm_dmabuf_disable, "ROCM_COPY_DMABUF~=no") |
There was a problem hiding this comment.
The test is added to test_md_dmabuf, which is instantiated for every MD, and then skipped by comparing GetParam().md_name to "rocm_cpy". This file already has a pattern for MD-specific tests (_UCT_MD_INSTANTIATE_TEST_CASE(test_gdr_copy, gdr_copy)).
why not put this in its own class instantiated with _UCT_MD_INSTANTIATE_TEST_CASE(test_rocm, rocm_cpy), like test_gdr_copy? then the md_name check is not needed.
| offset = 0; | ||
| ucs_warn("failed to export dmabuf handle for addr %p / %zu", addr, | ||
| length); | ||
| ucs_debug("failed to export dmabuf handle for addr %p / %zu: 0x%x", |
There was a problem hiding this comment.
Downgrading the export failure from ucs_warn to ucs_debug is right for the new try default, but it also silences the ROCM_COPY_DMABUF=yes case: uct_rocm_copy_md_open() only validates kernel support, so a runtime hsa_amd_portable_export_dmabuf() failure under an explicit yes now produces no output at the default log level.
with ROCM_COPY_DMABUF=yes an export failure is now completely silent - can we keep at least ucs_diag here, or pass the ternary down so yes still reports?
| @@ -542,10 +542,16 @@ UCS_TEST_P(test_md, mem_type_detect_mds) { | |||
| ASSERT_UCS_OK(status); | |||
| EXPECT_EQ(alloc_mem_type, mem_attr.mem_type); | |||
| if ((alloc_mem_type == UCS_MEMORY_TYPE_CUDA) || | |||
There was a problem hiding this comment.
minor: the nested if inside the memory-type list is harder to read than splitting ROCm out.
| if ((alloc_mem_type == UCS_MEMORY_TYPE_CUDA) || | |
| if (alloc_mem_type == UCS_MEMORY_TYPE_ROCM) { | |
| /* ROCm HSA may round sizeInBytes up to a page */ | |
| EXPECT_GE(mem_attr.alloc_length, buffer_size); | |
| EXPECT_EQ(address, mem_attr.base_address); | |
| } else if ((alloc_mem_type == UCS_MEMORY_TYPE_CUDA) || | |
| (alloc_mem_type == UCS_MEMORY_TYPE_ZE_HOST) || | |
| (alloc_mem_type == UCS_MEMORY_TYPE_ZE_DEVICE) || | |
| (alloc_mem_type == UCS_MEMORY_TYPE_ZE_MANAGED)) { | |
| EXPECT_EQ(buffer_size, mem_attr.alloc_length); | |
| EXPECT_EQ(address, mem_attr.base_address); |
|
Residual coverage gap: ROCm CI jobs ( Residual coverage gap: Residual coverage gap: nothing covers a failing |
|
🤖 Starting review — findings will be posted here when done. |
| } | ||
| } | ||
|
|
||
| UCS_TEST_P(test_md_dmabuf, rocm_dmabuf_disable, "ROCM_COPY_DMABUF~=no") |
There was a problem hiding this comment.
minor: pls put this in its own fixture instantiated for rocm_cpy only, like test_gdr_copy below (_UCT_MD_INSTANTIATE_TEST_CASE(test_rocm, rocm_cpy)). Then the ~= modifier and the md_name != "rocm_cpy" skip are both unnecessary, and test_md_dmabuf stays generic.
6812eeb to
7e3dddc
Compare
|
🤖 Starting review — findings will be posted here when done. |
|
|
||
| UCS_TEST_P(test_md_dmabuf, rocm_dmabuf_disable, "ROCM_COPY_DMABUF~=no") | ||
| { | ||
| if (GetParam().md_name != "rocm_cpy") { |
There was a problem hiding this comment.
minor: this guard is dead code — "ROCM_COPY_DMABUF~=no" uses SKIP_IF_NOT_EXIST, so any MD whose config table has no ROCM_COPY_DMABUF field is already skipped in init() before the body runs. pls drop the md_name check.
| uint64_t orig_mask = mem_attr_p->field_mask; | ||
| ucs_status_t status; | ||
|
|
||
| if (!md->have_dmabuf) { |
There was a problem hiding this comment.
maybe we can drop this wrapper? uct_cuda_copy_md_mem_query() keeps exporting the fd with CUDA_COPY_DMABUF=no and relies on dmabuf_mem_types == 0 to keep consumers from using it, so nothing in ucp reads these fields for rocm_cpy either. As written, rocm_cpy is the only MD that also mutates and restores the caller's field_mask, and the only thing that observes the difference is the new rocm_dmabuf_disable test.
|
Residual coverage gap: Residual coverage gap: the ROCm CI containers ( Residual coverage gap: nothing exercises a failing |
7e3dddc to
4a9f32a
Compare
|
🤖 Starting review — findings will be posted here when done. |
| uct_md_mem_attr_v2_t *mem_attr_p) | ||
| { | ||
| uct_rocm_copy_md_t *md = ucs_derived_of(uct_md, uct_rocm_copy_md_t); | ||
| uint64_t orig_mask = mem_attr_p->field_mask; |
There was a problem hiding this comment.
why not pass md->have_dmabuf into uct_rocm_base_mem_query() and gate uct_rocm_base_dmabuf_export() there? it has no other caller, so that drops the temporary mutation of the caller's mem_attr_p->field_mask and the manual re-init of the two fields.
Rationale: mem_attr_p is a caller-owned in/out struct; clearing bits in it and restoring them after the callee returns is fragile (it also makes the base run with an empty mask purely for its side effects, where an HSA failure is still propagated as a raw hsa_status_t). Passing the flag down keeps the base's behavior identical to before and removes ~25 lines.
| } | ||
| } | ||
|
|
||
| UCS_TEST_P(test_md_dmabuf, rocm_dmabuf_disable, "ROCM_COPY_DMABUF~=no") |
There was a problem hiding this comment.
minor: this test only applies to rocm_cpy, so a dedicated fixture with _UCT_MD_INSTANTIATE_TEST_CASE(test_rocm_copy, rocm_cpy) (as done for test_gdr_copy) is clearer than relying on ~= to skip the other 12 MD instances.
4a9f32a to
ba5cbb6
Compare
|
🤖 Starting review — findings will be posted here when done. |
| ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; | ||
| int dmabuf_fd; | ||
| int dmabuf_fd = UCT_DMABUF_FD_INVALID; | ||
| hsa_status_t status; |
There was a problem hiding this comment.
the early exit below returns status (an hsa_status_t) as ucs_status_t, so a uct_rocm_base_get_ptr_info() failure propagates e.g. 0x1002 up to ucp_memh_register_internal(). this path becomes reachable in gtest now that test_md_dmabuf runs for rocm_cpy and queries host memory too. can we return UCS_ERR_INVALID_ADDR instead?
| if (have_dmabuf) { | ||
| uct_rocm_base_dmabuf_export(base_addr, base_size, mem_type, | ||
| &dmabuf_fd, &dmabuf_offset); | ||
| } |
There was a problem hiding this comment.
minor: pls add a blank line after the block, per docs/CodeStyle.md ("Use blank line after if/else blocks").
| } | |
| } | |
ba5cbb6 to
cb780f7
Compare
|
🤖 Starting review — findings will be posted here when done. |
| @@ -447,7 +455,7 @@ ucs_status_t uct_rocm_base_mem_query(uct_md_h md, const void *addr, | |||
| &base_size, &hsa_mem_type, &agent, | |||
There was a problem hiding this comment.
minor: ucs_close_fd() already returns early for -1, so the extra check can go.
| &base_size, &hsa_mem_type, &agent, | |
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | |
| mem_attr_p->dmabuf_fd = dmabuf_fd; | |
| } else { | |
| ucs_close_fd(&dmabuf_fd); | |
| } |
cb780f7 to
2e8426a
Compare
|
🤖 Starting review — findings will be posted here when done. |
| &dmabuf_offset); | ||
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_OFFSET) { | ||
| mem_attr_p->dmabuf_offset = dmabuf_offset + | ||
| UCS_PTR_BYTE_DIFF(base_addr, addr); |
There was a problem hiding this comment.
dmabuf_offset is reported even when no dmabuf was exported. When have_dmabuf == 0 (e.g. ROCM_COPY_DMABUF=no), or when hsa_amd_portable_export_dmabuf() fails, dmabuf_fd stays UCT_DMABUF_FD_INVALID but we still report dmabuf_offset = addr - base_addr. Per the dmabuf_offset doc in uct.h (src/uct/api/uct.h:1686-1690) the offset is only defined relative to the dmabuf region identified by dmabuf_fd, so a non-zero value with an invalid fd has no meaning — please leave it 0 when nothing was exported. That also removes the hidden dependency behind EXPECT_EQ(0ul, mem_attr.dmabuf_offset) in the new dmabuf_disable test, which currently only passes because HSA happens to report agentBaseAddress == hipMalloc() pointer for that allocation. Note the fix cannot simply test dmabuf_fd == UCT_DMABUF_FD_INVALID at that point, because ucs_close_fd() resets it to -1 on the offset-only path; a separate "exported" flag or reordering is needed.
| ## Current | ||
| ### Features: | ||
| #### ROCM | ||
| * Enabled dmabuf by default for GPU-direct transfers, falling back to peermem when unavailable |
There was a problem hiding this comment.
minor: user-facing doc not updated for the ROCm default flip. Can we also update the dmabuf section in docs/source/faq.md ("What is needed for dmabuf support?")? It currently lists only Cuda requirements, and since this PR makes dmabuf the default for ROCm, the ROCm prerequisites (kernel with CONFIG_DMABUF_MOVE_NOTIFY/CONFIG_PCI_P2PDMA, hsa_amd_portable_export_dmabuf availability) belong there too.
|
Residual coverage gap: ROCm CI containers are build-only, so neither Residual coverage gap: nothing exercises a failing |
2e8426a to
6b6bb7b
Compare
|
🤖 Starting review — findings will be posted here when done. |
| if (mem_attr_p->field_mask & UCT_MD_MEM_ATTR_V2_FIELD_DMABUF_FD) { | ||
| mem_attr_p->dmabuf_fd = dmabuf_fd; | ||
| } else if (dmabuf_fd != UCT_DMABUF_FD_INVALID) { | ||
| ucs_close_fd(&dmabuf_fd); |
There was a problem hiding this comment.
minor: ucs_close_fd() already returns early for an invalid fd, so the extra check and the second sentence of the comment above can be dropped.
| ucs_close_fd(&dmabuf_fd); | |
| } else { | |
| ucs_close_fd(&dmabuf_fd); | |
| } |
| EXPECT_EQ(buffer_size, mem_attr.alloc_length); | ||
| /* ROCm HSA may round sizeInBytes up to a page */ | ||
| if (alloc_mem_type == UCS_MEMORY_TYPE_ROCM) { | ||
| EXPECT_GE(mem_attr.alloc_length, buffer_size); |
There was a problem hiding this comment.
minor: can we test ROCm in its own branch instead of nesting a second alloc_mem_type == UCS_MEMORY_TYPE_ROCM check inside the branch that just matched it?
|
Residual coverage gap: The ROCm CI containers ( Residual coverage gap: Residual coverage gap: Nothing exercises a failing |
…for GPU-direct when the kernel supports it, and keep peermem as the fallback on older kernels. Also report allocation-base offsets for interior ROCm addresses.
6b6bb7b to
29cb04c
Compare
|
🤖 Starting review — findings will be posted here when done. |
What?
Use dma-buf for GPU-direct when the kernel supports it, and keep peermem as the fallback on older kernels. Also report allocation-base offsets for interior ROCm addresses.
Why?
The rocm components in UCX have support for the dma-buf mechanism to enable direct data transfer between an RDMA NIC and a GPU. However, the user explicitly needs to request that support by setting UCX_ROCM_COPY_DMABUF=y, dma-buf support is not turned on by default.
How?
Starting from linux kernel > 6.0, the peermem mechanism that was used previously is not useable in the vast majority of deployments, necessitating using dma-buf instead. The goal of this story is to enable dma-buf by default in the rocm components of UCX, and ensure that if dma-buf support is not available (e.g. linux kernel 5.15 in most cases) we can fall back to the peermem mechanism if that is available.