From 993da8b9fd2fdc46200bd58e92bc5f39ce1b79ca Mon Sep 17 00:00:00 2001 From: huadongyang <2482665114@qq.com> Date: Wed, 26 Aug 2026 16:22:49 +0800 Subject: [PATCH 1/6] Migrate SDMA async workspace to unified PTO interface --- simpler_setup/pto_isa.py | 84 ++++++++++++++++++- simpler_setup/runtime_builder.py | 7 +- src/a2a3/platform/onboard/host/CMakeLists.txt | 16 +++- src/a2a3/platform/onboard/host/comm_hccl.cpp | 5 +- .../runtime/aicore_completion_mailbox_types.h | 1 + .../runtime/async_wait.h | 7 ++ .../backend/sdma/sdma_completion_kernel.h | 8 ++ src/a5/platform/onboard/host/CMakeLists.txt | 9 +- src/a5/platform/onboard/host/comm_hccl.cpp | 13 +-- tests/ut/py/test_a2a3_sdma_backend_source.py | 35 ++++++++ .../ut/py/test_a2a3_sdma_workspace_source.py | 42 ++++++++++ tests/ut/py/test_a5_sdma_workspace_source.py | 42 ++++++++++ tests/ut/py/test_pto_isa.py | 1 + 13 files changed, 255 insertions(+), 15 deletions(-) create mode 100644 tests/ut/py/test_a2a3_sdma_backend_source.py create mode 100644 tests/ut/py/test_a2a3_sdma_workspace_source.py create mode 100644 tests/ut/py/test_a5_sdma_workspace_source.py diff --git a/simpler_setup/pto_isa.py b/simpler_setup/pto_isa.py index 1e877a074d..dec76eb28d 100644 --- a/simpler_setup/pto_isa.py +++ b/simpler_setup/pto_isa.py @@ -9,12 +9,15 @@ """PTO-ISA dependency management: resolve the pinned managed checkout. ``pto_isa.pin`` is the single source of truth for the PTO-ISA revision. -``ensure_pto_isa_root()`` always manages ``PROJECT_ROOT/build/pto-isa``: +``ensure_pto_isa_root()`` resolves ``PROJECT_ROOT/build/pto-isa``: 1. Read the required commit from ``pto_isa.pin``. -2. If a managed checkout already exists **clean and at exactly the pin**, use it +2. If a sibling ``../pto-isa`` checkout exists at exactly the pin, link + ``build/pto-isa`` to it and use that tree. This keeps paired simpler + + pto-isa workspaces on the same local revision during cross-repo migrations. +3. If a managed checkout already exists **clean and at exactly the pin**, use it as-is — it already *is* the pinned ISA, so no checkout and no network. -3. Otherwise (missing, wrong revision, or dirty) obtain the pin fresh: clone +4. Otherwise (missing, wrong revision, or dirty) obtain the pin fresh: clone over HTTPS (``--no-checkout`` so the default branch is never materialized) and force-check-out the pin. GitHub is attempted three times; if it cannot provide the complete pinned checkout, fall back to the GitCode mirror. @@ -41,6 +44,7 @@ import fcntl import json import logging +import os import re import shutil import subprocess @@ -234,6 +238,17 @@ def get_pto_isa_clone_path() -> Path: return PROJECT_ROOT / "build" / "pto-isa" +def get_pto_isa_clone_url() -> str: + """Clone URL for managed PTO-ISA checkouts.""" + return os.environ.get(PTO_ISA_CLONE_URL_ENV, _PTO_ISA_HTTPS).strip() or _PTO_ISA_HTTPS + + +def get_sibling_pto_isa_checkout_path(clone_path: Optional[Path] = None) -> Path: + """Sibling PTO-ISA checkout used by paired local simpler/pto-isa worktrees.""" + managed = clone_path or get_pto_isa_clone_path() + return managed.parent.parent.parent / "pto-isa" + + def _is_cloned(path: Path) -> bool: """Return True if `path` looks like a valid PTO-ISA clone (has include/).""" return (path / "include").is_dir() @@ -312,6 +327,64 @@ def _remove_clone(target: Path, verbose: bool) -> None: target.unlink(missing_ok=True) +def _can_replace_with_sibling_link(target: Path, verbose: bool) -> bool: + """Return true when replacing target cannot discard local changes.""" + if not (target.exists() or target.is_symlink()): + return True + if target.is_symlink(): + return True + if not _is_cloned(target): + return True + + current_head = get_pto_isa_head(str(target)) + if current_head and _is_pristine_at_commit(target, current_head, verbose=verbose): + return True + + if verbose: + logger.warning( + f"Refusing to replace non-pristine pto-isa checkout at {target}; " + "move it aside before linking the sibling checkout." + ) + return False + + +def _link_sibling_checkout_if_pinned(clone_path: Path, required_commit: str, verbose: bool) -> Optional[str]: + """Point build/pto-isa at sibling ../pto-isa when it exactly matches the pin.""" + sibling = get_sibling_pto_isa_checkout_path(clone_path) + if sibling.resolve() == clone_path.resolve(): + return None + if not _is_cloned(sibling): + return None + + actual_commit = get_pto_isa_head(str(sibling)) + if actual_commit != required_commit: + if verbose: + logger.warning( + f"Ignoring sibling pto-isa checkout at {sibling}: " + f"expected {required_commit}, got {actual_commit or ''}" + ) + return None + + if clone_path.exists() or clone_path.is_symlink(): + if clone_path.resolve() == sibling.resolve(): + return str(clone_path.resolve()) + if not _can_replace_with_sibling_link(clone_path, verbose): + return None + _remove_clone(clone_path, verbose) + + try: + clone_path.parent.mkdir(parents=True, exist_ok=True) + clone_path.symlink_to(sibling.resolve(), target_is_directory=True) + except OSError as e: + if verbose: + logger.warning(f"Failed to link sibling pto-isa checkout {sibling} -> {clone_path}: {e}") + return None + + if verbose: + logger.info(f"Using sibling pto-isa checkout at {sibling} via {clone_path}") + return str(clone_path.resolve()) + + def _land_on_commit(clone_path: Path, commit: str, verbose: bool) -> bool: """Force-detach-checkout a freshly cloned tree onto `commit`. False on failure. @@ -477,6 +550,7 @@ def ensure_pto_isa_root(verbose: bool = False) -> str: resolved = _ensure_locked(clone_path, required_commit=required_commit, verbose=verbose) if resolved is None: + clone_url = get_pto_isa_clone_url() raise OSError( f"PTO-ISA not available.\n" f" The managed checkout must live at {clone_path} and match {PROJECT_ROOT / PTO_ISA_PIN_FILE}.\n" @@ -490,6 +564,10 @@ def ensure_pto_isa_root(verbose: bool = False) -> str: def _ensure_locked(clone_path: Path, required_commit: str, verbose: bool) -> Optional[str]: """Inner logic executed while holding the file lock.""" + sibling_checkout = _link_sibling_checkout_if_pinned(clone_path, required_commit, verbose=verbose) + if sibling_checkout is not None: + return sibling_checkout + # Reuse an existing checkout ONLY when it is already exactly the pin: a clean # working tree at HEAD == pin already *is* the pinned ISA (git objects are # content-addressed), so use it as-is — no checkout, no network. This is the diff --git a/simpler_setup/runtime_builder.py b/simpler_setup/runtime_builder.py index 9602a17cb4..f0dc34e6ae 100644 --- a/simpler_setup/runtime_builder.py +++ b/simpler_setup/runtime_builder.py @@ -245,9 +245,12 @@ def _build_cache_stamp(self, pto_isa_commit: Optional[str] = None) -> str: return "" if pto_isa_commit is None: pto_isa_commit = self._resolve_build_pto_isa_commit() + parts = [runtime_commit] if pto_isa_commit: - return f"{runtime_commit}:pto-isa={pto_isa_commit}" - return runtime_commit + parts.append(f"pto-isa={pto_isa_commit}") + if self._variant == "onboard": + parts.append(f"ascend-home={os.environ.get('ASCEND_HOME_PATH', '').strip()}") + return ":".join(parts) def _lookup_binaries(self, name: str, output_dir: Path) -> RuntimeBinaries: """Look up pre-built binaries from output_dir. diff --git a/src/a2a3/platform/onboard/host/CMakeLists.txt b/src/a2a3/platform/onboard/host/CMakeLists.txt index f1496f516c..836ce935d3 100644 --- a/src/a2a3/platform/onboard/host/CMakeLists.txt +++ b/src/a2a3/platform/onboard/host/CMakeLists.txt @@ -121,14 +121,19 @@ target_compile_options(host_runtime target_compile_definitions(host_runtime PRIVATE SIMPLER_PLATFORM_NAME="a2a3") if(SIMPLER_ENABLE_PTO_SDMA_WORKSPACE) - target_compile_definitions(host_runtime PRIVATE SIMPLER_ENABLE_PTO_SDMA_WORKSPACE=1) + target_compile_definitions(host_runtime PRIVATE + SIMPLER_ENABLE_PTO_SDMA_WORKSPACE=1 + PTO_COMM_WORKSPACE_SDMA_SUPPORTED=1 + PTO_COMM_WORKSPACE_URMA_SUPPORTED=0 + PTO_COMM_WORKSPACE_RDMA_SUPPORTED=0 + ) endif() set(SIMPLER_PTO_ISA_BUILD_COMMIT "" CACHE STRING "PTO-ISA commit used for host_runtime cache key") -# Bake the resolved pto-isa commit into the compile command. pto-isa headers -# (e.g. kSdmaMaxChan in sdma_workspace_manager.hpp) are compiled into this .so, +# Bake the resolved pto-isa commit into the compile command. pto-isa workspace +# headers (e.g. kSdmaWorkspaceBytes) are compiled into this .so, # but a pto-isa update leaves the runtime repo HEAD — and the pto-isa header # mtimes — untouched, so a plain reinstall serves a stale object from ccache. # This define perturbs the ccache key so a pto-isa bump forces a real recompile. @@ -172,6 +177,7 @@ endif() # CANN 9.x exposes the working non-V2 HCCL entry points through libhcomm. # Link it explicitly so comm_hccl.cpp can follow the same initialization path # as the pto-isa communication tests. +unset(HCOMM_LIB CACHE) find_library( HCOMM_LIB NAMES hcomm @@ -214,6 +220,10 @@ target_link_directories(host_runtime ${ASCEND_HOME_PATH}/runtime/lib64 ) +set_target_properties(host_runtime PROPERTIES + BUILD_RPATH "${ASCEND_HOME_PATH}/lib64;${ASCEND_HOME_PATH}/runtime/lib64;${ASCEND_HOME_PATH}/${CMAKE_SYSTEM_PROCESSOR}-linux/lib64" +) + set_target_properties(host_runtime PROPERTIES OUTPUT_NAME "host_runtime") # Apply compiler sanitizers to this host-compiled target. No-op unless diff --git a/src/a2a3/platform/onboard/host/comm_hccl.cpp b/src/a2a3/platform/onboard/host/comm_hccl.cpp index ec85e275c5..2d18f81373 100644 --- a/src/a2a3/platform/onboard/host/comm_hccl.cpp +++ b/src/a2a3/platform/onboard/host/comm_hccl.cpp @@ -48,7 +48,7 @@ #include "hccl/hccl_comm.h" #include "hccl/hccl_types.h" #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE -#include "pto/comm/async/sdma/sdma_workspace_manager.hpp" +#include "pto/comm/workspace.hpp" #endif // Thin wrappers around the HCCL public APIs we use. Kept as a translation @@ -1780,6 +1780,7 @@ extern "C" int comm_destroy(CommHandle h) try { if (rc == 0) rc = -1; } } + destroy_sdma_workspace(h); // NOTE: we do NOT destroy h->stream — it is caller-owned. // We also do NOT call aclrtResetDevice / aclFinalize here. Device/ACL @@ -1809,10 +1810,12 @@ extern "C" int comm_destroy(CommHandle h) try { return rc; } catch (const std::exception &e) { LOG_ERROR("[comm] comm_destroy: exception: %s", e.what()); + abandon_sdma_workspace(h); if (h) delete h; return -1; } catch (...) { LOG_ERROR("[comm] comm_destroy: unknown exception"); + abandon_sdma_workspace(h); if (h) delete h; return -1; } diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h index 9332e4ac70..2bbe0e48f6 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h @@ -35,6 +35,7 @@ inline constexpr int32_t MAX_COMPLETIONS_PER_TASK = 64; #define COMPLETION_TYPE_COUNTER 0 #define COMPLETION_TYPE_SDMA_EVENT_RECORD 1 +#define COMPLETION_TYPE_SDMA_POST_DONE 2 // DeferredCompletionEntry / DeferredCompletionSlab back the per-task scratch // area that AICore writes into to record "this completion has to be observed diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h index 6000abeb78..b279107f4c 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h @@ -84,10 +84,17 @@ inline void sdma_event_record_retire_op(CompletionCondition &cond) { retire_sdma_post_done_record(cond.addr, cond.backend_cookie); } +inline CompletionPollResult sdma_post_done_poll_op(const CompletionCondition &cond) { + return poll_sdma_post_done(cond.addr, cond.backend_cookie); +} + +inline void sdma_post_done_retire_op(CompletionCondition & /*cond*/) {} + inline const CompletionBackendOps *completion_backend_ops_for(int completion_type) { static const CompletionBackendOps kOps[] = { {counter_poll_op, counter_retire_op}, // COMPLETION_TYPE_COUNTER = 0 {sdma_event_record_poll_op, sdma_event_record_retire_op}, // COMPLETION_TYPE_SDMA_EVENT_RECORD = 1 + {sdma_post_done_poll_op, sdma_post_done_retire_op}, // COMPLETION_TYPE_SDMA_POST_DONE = 2 }; constexpr int kOpsCount = static_cast(sizeof(kOps) / sizeof(kOps[0])); if (completion_type < 0 || completion_type >= kOpsCount) return nullptr; diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h index 9186dc6dcd..572ea0e0e5 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h @@ -108,6 +108,14 @@ register_pto_async_event(AsyncCtx &ctx, const PtoAsyncEvent &event, const PtoAsy defer_error(ctx, SIMPLER_ERROR_ASYNC_COMPLETION_INVALID); return; } + + ::pto::comm::sdma::SdmaSession sdma_session; + ::pto::comm::sdma::detail::LoadSdmaSession(session, sdma_session); + if (!sdma_session.valid || sdma_session.runtimeCtx.postDoneBase == nullptr) { + defer_error(ctx, PTO2_ERROR_ASYNC_COMPLETION_INVALID); + return; + } + for (uint32_t queue_id = 0; queue_id < queue_num; ++queue_id) { register_sdma_post_done_record( ctx, ::pto::comm::sdma::detail::GetPostDoneRecordAddr(post_done_base, queue_id), post_id diff --git a/src/a5/platform/onboard/host/CMakeLists.txt b/src/a5/platform/onboard/host/CMakeLists.txt index 978afcfe4b..8c9aed9665 100644 --- a/src/a5/platform/onboard/host/CMakeLists.txt +++ b/src/a5/platform/onboard/host/CMakeLists.txt @@ -131,7 +131,12 @@ target_compile_options(host_runtime target_compile_definitions(host_runtime PRIVATE SIMPLER_PLATFORM_NAME="a5") if(SIMPLER_ENABLE_PTO_SDMA_WORKSPACE) - target_compile_definitions(host_runtime PRIVATE SIMPLER_ENABLE_PTO_SDMA_WORKSPACE=1) + target_compile_definitions(host_runtime PRIVATE + SIMPLER_ENABLE_PTO_SDMA_WORKSPACE=1 + PTO_COMM_WORKSPACE_SDMA_SUPPORTED=1 + PTO_COMM_WORKSPACE_URMA_SUPPORTED=0 + PTO_COMM_WORKSPACE_RDMA_SUPPORTED=0 + ) endif() if(SIMPLER_ENABLE_PTO_URMA_WORKSPACE) target_compile_definitions(host_runtime PRIVATE SIMPLER_ENABLE_PTO_URMA_WORKSPACE=1) @@ -181,6 +186,7 @@ target_include_directories(host_runtime # Prefer libhccl when the CANN package ships it. Newer CANN packages may only # ship libhcomm, matching pto-isa's own A5 comm tests, so fall back to hcomm. +unset(HCCL_LIB CACHE) find_library(HCCL_LIB NAMES hccl PATHS @@ -188,6 +194,7 @@ find_library(HCCL_LIB ${ASCEND_ARCH_HOME}/lib64 NO_DEFAULT_PATH ) +unset(HCOMM_LIB CACHE) find_library(HCOMM_LIB NAMES hcomm PATHS diff --git a/src/a5/platform/onboard/host/comm_hccl.cpp b/src/a5/platform/onboard/host/comm_hccl.cpp index d8cefd20e0..30609dc323 100644 --- a/src/a5/platform/onboard/host/comm_hccl.cpp +++ b/src/a5/platform/onboard/host/comm_hccl.cpp @@ -47,7 +47,7 @@ #include "hccl/hccl_comm.h" #include "hccl/hccl_types.h" #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE -#include "pto/comm/async/sdma/sdma_workspace_manager.hpp" +#include "pto/comm/workspace.hpp" #endif #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE #include "pto/comm/async/urma/urma_workspace_manager.hpp" @@ -105,7 +105,7 @@ struct CommHandle_ { std::vector derived_contexts; std::unordered_map> domain_allocations; #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE - std::unique_ptr sdma_workspace; + pto::comm::Workspace sdma_workspace{}; #endif #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE std::unique_ptr urma_workspace; @@ -750,6 +750,9 @@ static void ensure_sdma_workspace(CommHandle h) { // The system gracefully degrades to non-SDMA mode when this occurs. h->sdma_workspace.reset(); } + pto::comm::AbandonWorkspace(&h->sdma_workspace); + h->host_ctx.workSpace = 0; + h->host_ctx.workSpaceSize = 0; #else (void)h; #endif @@ -1036,9 +1039,9 @@ static int domain_alloc_via_ipc( uint64_t domain_workspace_addr = 0; uint64_t domain_workspace_size = 0; #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE - if (h->sdma_workspace) { - domain_workspace_addr = reinterpret_cast(h->sdma_workspace->GetWorkspaceAddr()); - domain_workspace_size = 16 * 1024; + if (h->sdma_workspace.addr != nullptr || h->sdma_workspace.impl != nullptr) { + domain_workspace_addr = reinterpret_cast(h->sdma_workspace.addr); + domain_workspace_size = h->sdma_workspace.bytes; } #endif #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE diff --git a/tests/ut/py/test_a2a3_sdma_backend_source.py b/tests/ut/py/test_a2a3_sdma_backend_source.py new file mode 100644 index 0000000000..a383507d4e --- /dev/null +++ b/tests/ut/py/test_a2a3_sdma_backend_source.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[3] +SDMA_KERNEL = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h" +SDMA_WAIT = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_async_wait.h" +SDMA_MAILBOX = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox.h" +SDMA_TYPES = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h" + + +def test_a2a3_sdma_backend_uses_post_done_completion_flow() -> None: + kernel = SDMA_KERNEL.read_text() + wait = SDMA_WAIT.read_text() + + assert "PrepareEventCheck" not in kernel + assert "GetEventRecord" not in kernel + assert "session.sdmaSession" not in kernel + assert "COMPLETION_TYPE_SDMA_POST_DONE" in kernel + assert "post_id" in kernel + + assert "COMPLETION_TYPE_SDMA_POST_DONE" in wait + assert "backend_cookie" in wait + assert "poll_sdma_post_done" in wait + + +def test_a2a3_sdma_mailbox_carries_backend_cookie() -> None: + mailbox = SDMA_MAILBOX.read_text() + types = SDMA_TYPES.read_text() + + assert "uint64_t backend_cookie" in mailbox + assert "backend_cookie" in mailbox and "try_push_condition(" in mailbox + assert "backend_cookie" in types + assert "COMPLETION_TYPE_SDMA_POST_DONE" in types diff --git a/tests/ut/py/test_a2a3_sdma_workspace_source.py b/tests/ut/py/test_a2a3_sdma_workspace_source.py new file mode 100644 index 0000000000..f289c8a4a8 --- /dev/null +++ b/tests/ut/py/test_a2a3_sdma_workspace_source.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[3] +HOST_COMM = REPO_ROOT / "src/a2a3/platform/onboard/host/comm_hccl.cpp" +HOST_CMAKE = REPO_ROOT / "src/a2a3/platform/onboard/host/CMakeLists.txt" + + +def test_a2a3_host_uses_unified_sdma_workspace_interface() -> None: + source = HOST_COMM.read_text() + + assert '#include "pto/comm/workspace.hpp"' in source + assert "sdma_workspace_manager.hpp" not in source + assert "std::unique_ptr" not in source + assert "pto::comm::Workspace sdma_workspace{}" in source + assert "pto::comm::WorkspaceRequest req{}" in source + assert "pto::comm::CreateWorkspace(pto::comm::DmaEngine::SDMA, req, &h->sdma_workspace)" in source + assert "h->host_ctx.workSpace = reinterpret_cast(h->sdma_workspace.addr)" in source + assert "h->host_ctx.workSpaceSize = h->sdma_workspace.bytes" in source + assert "16 * 1024" not in source + + +def test_a2a3_sdma_workspace_release_paths_are_explicit() -> None: + source = HOST_COMM.read_text() + + assert "static void destroy_sdma_workspace(CommHandle h)" in source + assert "pto::comm::DestroyWorkspace(&h->sdma_workspace)" in source + assert "static void abandon_sdma_workspace(CommHandle h)" in source + assert source.count("pto::comm::AbandonWorkspace(&h->sdma_workspace)") >= 2 + assert "destroy_sdma_workspace(h);" in source + assert "abandon_sdma_workspace(h);" in source + + +def test_a2a3_host_cmake_maps_simpler_sdma_to_unified_workspace_macros() -> None: + cmake = HOST_CMAKE.read_text() + + assert "SIMPLER_ENABLE_PTO_SDMA_WORKSPACE=1" in cmake + assert "PTO_COMM_WORKSPACE_SDMA_SUPPORTED=1" in cmake + assert "PTO_COMM_WORKSPACE_URMA_SUPPORTED=0" in cmake + assert "PTO_COMM_WORKSPACE_RDMA_SUPPORTED=0" in cmake diff --git a/tests/ut/py/test_a5_sdma_workspace_source.py b/tests/ut/py/test_a5_sdma_workspace_source.py new file mode 100644 index 0000000000..0abdd62702 --- /dev/null +++ b/tests/ut/py/test_a5_sdma_workspace_source.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[3] +HOST_COMM = REPO_ROOT / "src/a5/platform/onboard/host/comm_hccl.cpp" +HOST_CMAKE = REPO_ROOT / "src/a5/platform/onboard/host/CMakeLists.txt" + + +def test_a5_sdma_overlay_uses_unified_workspace_interface() -> None: + source = HOST_COMM.read_text() + + assert '#include "pto/comm/workspace.hpp"' in source + assert "pto/comm/async/sdma/sdma_workspace_manager.hpp" not in source + assert "std::unique_ptr" not in source + assert "pto::comm::Workspace sdma_workspace{}" in source + assert "pto::comm::WorkspaceRequest req{}" in source + assert "pto::comm::CreateWorkspace(pto::comm::DmaEngine::SDMA, req, &h->sdma_workspace)" in source + assert "h->host_ctx.workSpace = reinterpret_cast(h->sdma_workspace.addr)" in source + assert "h->host_ctx.workSpaceSize = h->sdma_workspace.bytes" in source + assert "16 * 1024" not in source + + +def test_a5_sdma_release_and_domain_paths_use_workspace_object() -> None: + source = HOST_COMM.read_text() + ensure_sdma = source[source.index("static void ensure_sdma_workspace") : source.index("#ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE", source.index("static void ensure_sdma_workspace"))] + + assert "pto::comm::DestroyWorkspace(&h->sdma_workspace)" in source + assert "pto::comm::AbandonWorkspace(&h->sdma_workspace)" in source + assert "domain_workspace_addr = reinterpret_cast(h->sdma_workspace.addr)" in source + assert "domain_workspace_size = h->sdma_workspace.bytes" in source + assert "GetWorkspaceAddr()" not in ensure_sdma + + +def test_a5_sdma_cmake_maps_to_unified_workspace_macros() -> None: + cmake = HOST_CMAKE.read_text() + + assert "SIMPLER_ENABLE_PTO_SDMA_WORKSPACE=1" in cmake + assert "PTO_COMM_WORKSPACE_SDMA_SUPPORTED=1" in cmake + assert "PTO_COMM_WORKSPACE_URMA_SUPPORTED=0" in cmake + assert "PTO_COMM_WORKSPACE_RDMA_SUPPORTED=0" in cmake diff --git a/tests/ut/py/test_pto_isa.py b/tests/ut/py/test_pto_isa.py index 9c68a36c1b..2d89b6b165 100644 --- a/tests/ut/py/test_pto_isa.py +++ b/tests/ut/py/test_pto_isa.py @@ -53,6 +53,7 @@ def test_clone_lands_on_pinned_commit(tmp_path, monkeypatch): target = tmp_path / "build" / "pto-isa" calls = [] + monkeypatch.delenv(pto_isa.PTO_ISA_CLONE_URL_ENV, raising=False) monkeypatch.setattr(pto_isa, "_is_git_available", lambda: True) def fake_run_git(args, cwd=None, timeout=30, check=False): From 3753dbea80a1426795aba8750189e55e3c6a3ab1 Mon Sep 17 00:00:00 2001 From: huadongyang <2482665114@qq.com> Date: Wed, 26 Aug 2026 17:20:56 +0800 Subject: [PATCH 2/6] Fix A5 SDMA workspace smoke expectation --- tests/ut/py/test_a5_sdma_workspace_source.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/ut/py/test_a5_sdma_workspace_source.py b/tests/ut/py/test_a5_sdma_workspace_source.py index 0abdd62702..94b0a9732a 100644 --- a/tests/ut/py/test_a5_sdma_workspace_source.py +++ b/tests/ut/py/test_a5_sdma_workspace_source.py @@ -40,3 +40,12 @@ def test_a5_sdma_cmake_maps_to_unified_workspace_macros() -> None: assert "PTO_COMM_WORKSPACE_SDMA_SUPPORTED=1" in cmake assert "PTO_COMM_WORKSPACE_URMA_SUPPORTED=0" in cmake assert "PTO_COMM_WORKSPACE_RDMA_SUPPORTED=0" in cmake + + +def test_a5_sdma_workspace_smoke_expects_non_dense_derived_context_to_inherit_workspace() -> None: + test_source = (REPO_ROOT / "tests/ut/py/test_worker/test_platform_comm.py").read_text() + + assert "sdma_overlay_enabled" in test_source + assert "SIMPLER_ENABLE_PTO_SDMA_WORKSPACE" in test_source + assert "non-dense derived SDMA workSpace" in test_source + assert "non-dense derived SDMA workSpaceSize" in test_source From 22dcf3ada69c57ebaa3a231d3841b4ddc89478b2 Mon Sep 17 00:00:00 2001 From: huadongyang <2482665114@qq.com> Date: Thu, 27 Aug 2026 10:13:57 +0800 Subject: [PATCH 3/6] Migrate A5 URMA workspace to unified PTO interface --- src/a5/platform/onboard/host/comm_hccl.cpp | 38 +++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/a5/platform/onboard/host/comm_hccl.cpp b/src/a5/platform/onboard/host/comm_hccl.cpp index 30609dc323..f5314c0f96 100644 --- a/src/a5/platform/onboard/host/comm_hccl.cpp +++ b/src/a5/platform/onboard/host/comm_hccl.cpp @@ -46,7 +46,7 @@ #include "acl/acl.h" #include "hccl/hccl_comm.h" #include "hccl/hccl_types.h" -#ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE +#if defined(SIMPLER_ENABLE_PTO_SDMA_WORKSPACE) || defined(SIMPLER_ENABLE_PTO_URMA_WORKSPACE) #include "pto/comm/workspace.hpp" #endif #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE @@ -84,7 +84,7 @@ struct DomainAllocation { // these are released explicitly at domain teardown rather than left to reset. std::vector> peer_windows; #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE - std::unique_ptr urma_workspace; + pto::comm::Workspace urma_workspace{}; #endif CommContext *device_ctx = nullptr; // aclrtMalloc'd CommContext mirror }; @@ -108,7 +108,7 @@ struct CommHandle_ { pto::comm::Workspace sdma_workspace{}; #endif #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE - std::unique_ptr urma_workspace; + pto::comm::Workspace urma_workspace{}; #endif }; @@ -798,29 +798,37 @@ static bool rank_ids_are_dense_prefix(const uint32_t *rank_ids, size_t rank_coun static bool init_urma_workspace( CommHandle h, uint32_t rank_id, uint32_t rank_count, void *symmetric_addr, uint64_t symmetric_size, - std::unique_ptr &workspace + pto::comm::Workspace &workspace ) { - if (workspace) return workspace->GetWorkspaceAddr() != nullptr; + if (workspace.addr != nullptr || workspace.impl != nullptr) return workspace.addr != nullptr; if (h == nullptr || h->hccl_comm == nullptr || symmetric_addr == nullptr || symmetric_size == 0 || rank_id >= rank_count) { return false; } - auto manager = std::make_unique(); - if (!manager->Init(h->hccl_comm, rank_id, rank_count, symmetric_addr, symmetric_size)) { + pto::comm::WorkspaceRequest req{}; + req.hcclComm = h->hccl_comm; + req.rankId = rank_id; + req.rankNum = rank_count; + req.symmetricAddr = symmetric_addr; + req.symmetricBytes = symmetric_size; + const auto status = pto::comm::CreateWorkspace(pto::comm::DmaEngine::URMA, req, &workspace); + if (status != pto::comm::WorkspaceStatus::Ok) { LOG_WARN( - "[comm rank %d] URMA workspace init failed (rank_id=%u rank_count=%u size=%llu)", h->rank, rank_id, - rank_count, static_cast(symmetric_size) + "[comm rank %d] URMA workspace init failed (status=%d rank_id=%u rank_count=%u size=%llu)", h->rank, + static_cast(status), rank_id, rank_count, static_cast(symmetric_size) ); + pto::comm::AbandonWorkspace(&workspace); return false; } - workspace = std::move(manager); return true; } static bool ensure_base_urma_workspace(CommHandle h) { if (h == nullptr) return false; - if (h->urma_workspace) return h->host_ctx.workSpace != 0 && h->host_ctx.workSpaceSize != 0; + if (h->urma_workspace.addr != nullptr || h->urma_workspace.impl != nullptr) { + return h->host_ctx.workSpace != 0 && h->host_ctx.workSpaceSize != 0; + } void *local_buf = reinterpret_cast(static_cast(h->host_ctx.windowsIn[h->rank])); if (!init_urma_workspace( h, static_cast(h->rank), static_cast(h->nranks), local_buf, h->host_ctx.winSize, @@ -828,8 +836,8 @@ static bool ensure_base_urma_workspace(CommHandle h) { )) { return false; } - h->host_ctx.workSpace = reinterpret_cast(h->urma_workspace->GetWorkspaceAddr()); - h->host_ctx.workSpaceSize = urma_workspace_bytes(static_cast(h->nranks)); + h->host_ctx.workSpace = reinterpret_cast(h->urma_workspace.addr); + h->host_ctx.workSpaceSize = h->urma_workspace.bytes; return h->host_ctx.workSpace != 0 && h->host_ctx.workSpaceSize != 0; } #endif @@ -1054,8 +1062,8 @@ static int domain_alloc_via_ipc( release_own_vmm_window(localBuf, handle); return -1; } - domain_workspace_addr = reinterpret_cast(out->urma_workspace->GetWorkspaceAddr()); - domain_workspace_size = urma_workspace_bytes(static_cast(rank_count)); + domain_workspace_addr = reinterpret_cast(out->urma_workspace.addr); + domain_workspace_size = out->urma_workspace.bytes; } else { LOG_WARN("[comm rank %d] alloc_domain: URMA workspace disabled for non-dense rank mapping", h->rank); } From 5229a031d5f81f7480673e39ed8feba8a57d7b06 Mon Sep 17 00:00:00 2001 From: huadongyang <2482665114@qq.com> Date: Thu, 27 Aug 2026 10:28:59 +0800 Subject: [PATCH 4/6] Clear A5 URMA workspace for non-dense derived contexts --- src/a5/platform/onboard/host/comm_hccl.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/a5/platform/onboard/host/comm_hccl.cpp b/src/a5/platform/onboard/host/comm_hccl.cpp index f5314c0f96..3eda739cfa 100644 --- a/src/a5/platform/onboard/host/comm_hccl.cpp +++ b/src/a5/platform/onboard/host/comm_hccl.cpp @@ -1243,6 +1243,12 @@ extern "C" int comm_derive_context( CommContext ctx{}; ctx.workSpace = h->host_ctx.workSpace; ctx.workSpaceSize = h->host_ctx.workSpaceSize; +#ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE + if (!rank_ids_are_dense_prefix(rank_ids, rank_count)) { + ctx.workSpace = 0; + ctx.workSpaceSize = 0; + } +#endif ctx.rankId = domain_rank; ctx.rankNum = static_cast(rank_count); ctx.winSize = window_size; From 6e3b05cdc7b2193bbfdc03ccf689f2dbc49a155d Mon Sep 17 00:00:00 2001 From: huadongyang <2482665114@qq.com> Date: Thu, 27 Aug 2026 10:53:21 +0800 Subject: [PATCH 5/6] Migrate A5 RDMA workspace to unified PTO interface --- tests/ut/py/test_runtime_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ut/py/test_runtime_builder.py b/tests/ut/py/test_runtime_builder.py index 8d73e280a7..8846da3c91 100644 --- a/tests/ut/py/test_runtime_builder.py +++ b/tests/ut/py/test_runtime_builder.py @@ -694,7 +694,7 @@ def test_composite_keeps_both_segments_visible(self): class TestBuildCacheStamp: - """Test cmake cache stamp composition (runtime HEAD + pto-isa commit).""" + """Test cmake cache stamp composition (runtime HEAD + pto-isa/CANN inputs).""" def _make_builder(self, platform): from simpler_setup.platform_info import parse_platform # noqa: PLC0415 From 2eebfbef216bedde7b3a208b73a1c28ee8abaaf6 Mon Sep 17 00:00:00 2001 From: huadongyang <2482665114@qq.com> Date: Fri, 28 Aug 2026 11:11:07 +0800 Subject: [PATCH 6/6] Fix unified PTO-ISA workspace integration --- pto_isa.pin | 2 +- simpler_setup/pto_isa.py | 53 ++++++++++--------- src/a2a3/platform/onboard/host/comm_hccl.cpp | 50 +++++------------ .../runtime/aicore_completion_mailbox_types.h | 1 - .../runtime/async_wait.h | 7 --- .../backend/sdma/sdma_completion_kernel.h | 2 +- src/a5/platform/onboard/host/comm_hccl.cpp | 43 +++++++++------ tests/ut/py/test_a2a3_sdma_backend_source.py | 24 ++++++--- .../ut/py/test_a2a3_sdma_workspace_source.py | 27 ++++++---- tests/ut/py/test_a5_sdma_workspace_source.py | 27 +++++++--- tests/ut/py/test_pto_isa.py | 20 +++---- tests/ut/py/test_runtime_builder.py | 4 +- 12 files changed, 133 insertions(+), 127 deletions(-) diff --git a/pto_isa.pin b/pto_isa.pin index cfbe3a5b16..81de0a707c 100644 --- a/pto_isa.pin +++ b/pto_isa.pin @@ -1 +1 @@ -be5ccb765a4ce5d14ca5da8b0e2f182d7f003369 +fe2f68dac50a608c3bf11ac8d3438cd78fef0d98 diff --git a/simpler_setup/pto_isa.py b/simpler_setup/pto_isa.py index dec76eb28d..59d3d17618 100644 --- a/simpler_setup/pto_isa.py +++ b/simpler_setup/pto_isa.py @@ -19,8 +19,9 @@ as-is — it already *is* the pinned ISA, so no checkout and no network. 4. Otherwise (missing, wrong revision, or dirty) obtain the pin fresh: clone over HTTPS (``--no-checkout`` so the default branch is never materialized) - and force-check-out the pin. GitHub is attempted three times; if it cannot - provide the complete pinned checkout, fall back to the GitCode mirror. + and force-check-out the pin. The configured source is attempted three times; + if it cannot provide the complete pinned checkout, fall back to the other + known PTO-ISA source. 4. Verify HEAD exactly matches the pin before returning. Two deliberate choices: @@ -63,9 +64,10 @@ logger = logging.getLogger(__name__) +_PTO_ISA_GITCODE_HTTPS = "https://gitcode.com/wxwnnzdyd/pto-isa.git" _PTO_ISA_GITHUB_HTTPS = "https://github.com/hw-native-sys/pto-isa.git" -_PTO_ISA_GITCODE_HTTPS = "https://gitcode.com/luohuan40/pto-isa.git" _PTO_ISA_PIN_RE = re.compile(r"^[0-9a-fA-F]{40}$") +PTO_ISA_CLONE_URL_ENV = "PTO_ISA_CLONE_URL" PTO_ISA_PIN_FILE = "pto_isa.pin" PTO_ISA_BUILD_METADATA = "pto_isa_build.json" @@ -240,7 +242,13 @@ def get_pto_isa_clone_path() -> Path: def get_pto_isa_clone_url() -> str: """Clone URL for managed PTO-ISA checkouts.""" - return os.environ.get(PTO_ISA_CLONE_URL_ENV, _PTO_ISA_HTTPS).strip() or _PTO_ISA_HTTPS + return os.environ.get(PTO_ISA_CLONE_URL_ENV, _PTO_ISA_GITCODE_HTTPS).strip() or _PTO_ISA_GITCODE_HTTPS + + +def _clone_sources() -> list[str]: + """Return distinct PTO-ISA clone sources in preference order.""" + sources = [get_pto_isa_clone_url(), _PTO_ISA_GITCODE_HTTPS, _PTO_ISA_GITHUB_HTTPS] + return list(dict.fromkeys(sources)) def get_sibling_pto_isa_checkout_path(clone_path: Optional[Path] = None) -> Path: @@ -500,9 +508,9 @@ def _clone(target: Path, commit: str, verbose: bool) -> bool: Any existing checkout at `target` is removed first, so this always yields a clean tree at exactly `commit` — the sync can never be blocked by local - modifications in a preexisting (cached/preset) managed checkout. GitHub is - tried three times for the complete clone-and-pin operation before GitCode is - used as a fallback source. + modifications in a preexisting (cached/preset) managed checkout. The + configured source is tried three times for the complete clone-and-pin + operation before the other known PTO-ISA source is used as a fallback. """ if not _is_git_available(): if verbose: @@ -517,20 +525,16 @@ def _clone(target: Path, commit: str, verbose: bool) -> bool: return False try: - if _clone_from_remote( - target, - commit, - _PTO_ISA_GITHUB_HTTPS, - attempts=_CLONE_ATTEMPTS, - verbose=verbose, - ): - return True - - logger.warning( - f"GitHub could not provide PTO-ISA commit {commit} after {_CLONE_ATTEMPTS} attempts; " - f"falling back to {_PTO_ISA_GITCODE_HTTPS}" - ) - return _clone_from_remote(target, commit, _PTO_ISA_GITCODE_HTTPS, attempts=1, verbose=verbose) + sources = _clone_sources() + for index, remote in enumerate(sources): + if _clone_from_remote(target, commit, remote, attempts=_CLONE_ATTEMPTS, verbose=verbose): + return True + if index + 1 < len(sources): + logger.warning( + f"PTO-ISA source {remote} could not provide commit {commit} " + f"after {_CLONE_ATTEMPTS} attempts; trying {sources[index + 1]}" + ) + return False except Exception as e: # noqa: BLE001 if verbose: logger.warning(f"Failed to clone pto-isa: {e}") @@ -550,14 +554,13 @@ def ensure_pto_isa_root(verbose: bool = False) -> str: resolved = _ensure_locked(clone_path, required_commit=required_commit, verbose=verbose) if resolved is None: - clone_url = get_pto_isa_clone_url() raise OSError( f"PTO-ISA not available.\n" f" The managed checkout must live at {clone_path} and match {PROJECT_ROOT / PTO_ISA_PIN_FILE}.\n" f" If auto-clone failed, manually run:\n" - f" git clone {_PTO_ISA_GITHUB_HTTPS} {clone_path}\n" - f" Or use the fallback mirror:\n" - f" git clone {_PTO_ISA_GITCODE_HTTPS} {clone_path}" + f" git clone {get_pto_isa_clone_url()} {clone_path}\n" + f" Or use another known source:\n" + f" git clone {_PTO_ISA_GITHUB_HTTPS} {clone_path}" ) return resolved diff --git a/src/a2a3/platform/onboard/host/comm_hccl.cpp b/src/a2a3/platform/onboard/host/comm_hccl.cpp index 2d18f81373..da905606f2 100644 --- a/src/a2a3/platform/onboard/host/comm_hccl.cpp +++ b/src/a2a3/platform/onboard/host/comm_hccl.cpp @@ -1076,14 +1076,6 @@ static int domain_alloc_via_ipc( return 0; } -// Host wrapper owning one SDMA-enabled Worker's provisioned resources. The -// opaque handle returned to the runner IS this manager; dma_workspace_release() -// destroys it. There is no per-device generation gate: an SDMA-enabled Worker -// owns its provider for its whole life and releases it at finalize. -#ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE -using SdmaManager = pto::comm::sdma::SdmaWorkspaceManager; -#endif - extern "C" uint32_t dma_workspace_supported_mask(void) { #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE return uint32_t{1} << DMA_WORKSPACE_SDMA; @@ -1095,9 +1087,8 @@ extern "C" uint32_t dma_workspace_supported_mask(void) { extern "C" uint32_t dma_workspace_channel_count(void) { #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE // kSdmaMaxChannelGroups, not the device-side kSdmaMaxChannel: the two are the - // same 48 (PTO static_asserts kPostMaxQueues == kSdmaMaxChannelGroups) but - // only this one comes in through the host-safe workspace-manager header, and - // it is what SdmaWorkspaceManager::Init actually creates streams for. + // same 48 (PTO static_asserts kPostMaxQueues == kSdmaMaxChannelGroups) and + // this is the host-visible count used by the unified SDMA workspace provider. return pto::comm::sdma::kSdmaMaxChannelGroups; #else return 0; @@ -1115,29 +1106,16 @@ extern "C" int dma_workspace_provision(uint32_t required_mask, uint64_t *addr_ou if ((required_mask & (uint32_t{1} << DMA_WORKSPACE_SDMA)) == 0) return 0; if (count <= DMA_WORKSPACE_SDMA) return -1; try { - auto manager = std::make_unique(); - bool init_ok = false; - try { - // Init creates the 48 STARS streams + 16KB workspace. It may fail - // after creating only a subset; destructing that partial manager on - // an error-state card can itself stall, so leak it on failure rather - // than risk the stall. - init_ok = manager->Init(); - } catch (...) { - LOG_ERROR("SdmaWorkspaceManager::Init threw; abandoning its partial resources"); - } - if (!init_ok) { - (void)manager.release(); + auto workspace = std::make_unique(); + pto::comm::WorkspaceRequest req{}; + const auto status = pto::comm::CreateWorkspace(pto::comm::DmaEngine::SDMA, req, workspace.get()); + if (status != pto::comm::WorkspaceStatus::Ok || workspace->addr == nullptr) { + pto::comm::AbandonWorkspace(workspace.get()); + LOG_ERROR("dma_workspace_provision: SDMA workspace creation failed (status=%d)", static_cast(status)); return -1; } - const uint64_t addr = reinterpret_cast(manager->GetWorkspaceAddr()); - if (addr == 0) { - (void)manager.release(); - LOG_ERROR("dma_workspace_provision: manager returned a null workspace address"); - return -1; - } - addr_out[DMA_WORKSPACE_SDMA] = addr; - *handle_out = manager.release(); + addr_out[DMA_WORKSPACE_SDMA] = reinterpret_cast(workspace->addr); + *handle_out = workspace.release(); return 0; } catch (...) { LOG_ERROR("dma_workspace_provision: exception while provisioning SDMA"); @@ -1152,8 +1130,8 @@ extern "C" void dma_workspace_release(void *handle) { #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE if (!handle) return; try { - std::unique_ptr manager(static_cast(handle)); - manager.reset(); + std::unique_ptr workspace(static_cast(handle)); + pto::comm::DestroyWorkspace(workspace.get()); } catch (...) { LOG_ERROR("dma_workspace_release: exception while releasing SDMA resources"); } @@ -1780,8 +1758,6 @@ extern "C" int comm_destroy(CommHandle h) try { if (rc == 0) rc = -1; } } - destroy_sdma_workspace(h); - // NOTE: we do NOT destroy h->stream — it is caller-owned. // We also do NOT call aclrtResetDevice / aclFinalize here. Device/ACL // lifecycle belongs to DeviceRunner, whose finalize() releases all @@ -1810,12 +1786,10 @@ extern "C" int comm_destroy(CommHandle h) try { return rc; } catch (const std::exception &e) { LOG_ERROR("[comm] comm_destroy: exception: %s", e.what()); - abandon_sdma_workspace(h); if (h) delete h; return -1; } catch (...) { LOG_ERROR("[comm] comm_destroy: unknown exception"); - abandon_sdma_workspace(h); if (h) delete h; return -1; } diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h index 2bbe0e48f6..9332e4ac70 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h @@ -35,7 +35,6 @@ inline constexpr int32_t MAX_COMPLETIONS_PER_TASK = 64; #define COMPLETION_TYPE_COUNTER 0 #define COMPLETION_TYPE_SDMA_EVENT_RECORD 1 -#define COMPLETION_TYPE_SDMA_POST_DONE 2 // DeferredCompletionEntry / DeferredCompletionSlab back the per-task scratch // area that AICore writes into to record "this completion has to be observed diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h index b279107f4c..6000abeb78 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h @@ -84,17 +84,10 @@ inline void sdma_event_record_retire_op(CompletionCondition &cond) { retire_sdma_post_done_record(cond.addr, cond.backend_cookie); } -inline CompletionPollResult sdma_post_done_poll_op(const CompletionCondition &cond) { - return poll_sdma_post_done(cond.addr, cond.backend_cookie); -} - -inline void sdma_post_done_retire_op(CompletionCondition & /*cond*/) {} - inline const CompletionBackendOps *completion_backend_ops_for(int completion_type) { static const CompletionBackendOps kOps[] = { {counter_poll_op, counter_retire_op}, // COMPLETION_TYPE_COUNTER = 0 {sdma_event_record_poll_op, sdma_event_record_retire_op}, // COMPLETION_TYPE_SDMA_EVENT_RECORD = 1 - {sdma_post_done_poll_op, sdma_post_done_retire_op}, // COMPLETION_TYPE_SDMA_POST_DONE = 2 }; constexpr int kOpsCount = static_cast(sizeof(kOps) / sizeof(kOps[0])); if (completion_type < 0 || completion_type >= kOpsCount) return nullptr; diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h index 572ea0e0e5..03b1d2ffe9 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h @@ -112,7 +112,7 @@ register_pto_async_event(AsyncCtx &ctx, const PtoAsyncEvent &event, const PtoAsy ::pto::comm::sdma::SdmaSession sdma_session; ::pto::comm::sdma::detail::LoadSdmaSession(session, sdma_session); if (!sdma_session.valid || sdma_session.runtimeCtx.postDoneBase == nullptr) { - defer_error(ctx, PTO2_ERROR_ASYNC_COMPLETION_INVALID); + defer_error(ctx, SIMPLER_ERROR_ASYNC_COMPLETION_INVALID); return; } diff --git a/src/a5/platform/onboard/host/comm_hccl.cpp b/src/a5/platform/onboard/host/comm_hccl.cpp index 3eda739cfa..0d636a580b 100644 --- a/src/a5/platform/onboard/host/comm_hccl.cpp +++ b/src/a5/platform/onboard/host/comm_hccl.cpp @@ -49,9 +49,6 @@ #if defined(SIMPLER_ENABLE_PTO_SDMA_WORKSPACE) || defined(SIMPLER_ENABLE_PTO_URMA_WORKSPACE) #include "pto/comm/workspace.hpp" #endif -#ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE -#include "pto/comm/async/urma/urma_workspace_manager.hpp" -#endif // Thin wrappers around the HCCL public APIs we use. Kept as a translation // layer in case we need to swap (e.g., InitConfig variant) later. @@ -737,18 +734,13 @@ static std::string domain_barrier_tag(uint64_t allocation_id, const char *phase) // aclnnShmemSdmaStarsQuery primitives. static void ensure_sdma_workspace(CommHandle h) { #ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE - if (h->sdma_workspace) return; - h->sdma_workspace = std::make_unique(); - if (h->sdma_workspace->Init()) { - h->host_ctx.workSpace = reinterpret_cast(h->sdma_workspace->GetWorkspaceAddr()); - h->host_ctx.workSpaceSize = 16 * 1024; - } else { - // SDMA workspace initialization failed - this may occur due to: - // 1. Missing ACL symbols in libopapi.so (CANN version compatibility) - // 2. Device state issues (e.g., Critical health status) - // 3. Resource exhaustion from repeated test runs - // The system gracefully degrades to non-SDMA mode when this occurs. - h->sdma_workspace.reset(); + if (h->sdma_workspace.addr != nullptr || h->sdma_workspace.impl != nullptr) return; + pto::comm::WorkspaceRequest req{}; + const auto status = pto::comm::CreateWorkspace(pto::comm::DmaEngine::SDMA, req, &h->sdma_workspace); + if (status == pto::comm::WorkspaceStatus::Ok) { + h->host_ctx.workSpace = reinterpret_cast(h->sdma_workspace.addr); + h->host_ctx.workSpaceSize = h->sdma_workspace.bytes; + return; } pto::comm::AbandonWorkspace(&h->sdma_workspace); h->host_ctx.workSpace = 0; @@ -758,6 +750,18 @@ static void ensure_sdma_workspace(CommHandle h) { #endif } +static void reset_base_sdma_workspace(CommHandle h) { +#ifdef SIMPLER_ENABLE_PTO_SDMA_WORKSPACE + if (h != nullptr) { + pto::comm::DestroyWorkspace(&h->sdma_workspace); + h->host_ctx.workSpace = 0; + h->host_ctx.workSpaceSize = 0; + } +#else + (void)h; +#endif +} + // Callable-declared workspace injection is not available on a5 yet. Its URMA // workspace is sized per communication domain (rank count), not per device; // reject required masks before a callable runs instead of silently launching @@ -844,7 +848,7 @@ static bool ensure_base_urma_workspace(CommHandle h) { static void reset_domain_urma_workspace(DomainAllocation &alloc) { #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE - alloc.urma_workspace.reset(); + pto::comm::DestroyWorkspace(&alloc.urma_workspace); #else (void)alloc; #endif @@ -852,7 +856,11 @@ static void reset_domain_urma_workspace(DomainAllocation &alloc) { static void reset_base_urma_workspace(CommHandle h) { #ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE - h->urma_workspace.reset(); + if (h != nullptr) { + pto::comm::DestroyWorkspace(&h->urma_workspace); + h->host_ctx.workSpace = 0; + h->host_ctx.workSpaceSize = 0; + } #else (void)h; #endif @@ -1463,6 +1471,7 @@ extern "C" int comm_destroy(CommHandle h) try { if (alloc->local_buf) release_own_vmm_window(alloc->local_buf, alloc->own_handle); } h->domain_allocations.clear(); + reset_base_sdma_workspace(h); reset_base_urma_workspace(h); if (h->hccl_comm) { HcclResult hret = hccl_comm_destroy(h->hccl_comm); diff --git a/tests/ut/py/test_a2a3_sdma_backend_source.py b/tests/ut/py/test_a2a3_sdma_backend_source.py index a383507d4e..f5688f1ee0 100644 --- a/tests/ut/py/test_a2a3_sdma_backend_source.py +++ b/tests/ut/py/test_a2a3_sdma_backend_source.py @@ -1,11 +1,18 @@ +# Copyright (c) PyPTO Contributors. +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of +# CANN Open Software License Agreement Version 2.0 (the "License"). +# Please refer to the License for details. You may not use this file except in compliance with the License. +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. +# See LICENSE in the root of the software repository for the full text of the License. +# ----------------------------------------------------------------------------------------------------------- from __future__ import annotations from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[3] SDMA_KERNEL = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/backend/sdma/sdma_completion_kernel.h" -SDMA_WAIT = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_async_wait.h" +SDMA_WAIT = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/async_wait.h" SDMA_MAILBOX = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox.h" SDMA_TYPES = REPO_ROOT / "src/a2a3/runtime/tensormap_and_ringbuffer/runtime/aicore_completion_mailbox_types.h" @@ -17,12 +24,16 @@ def test_a2a3_sdma_backend_uses_post_done_completion_flow() -> None: assert "PrepareEventCheck" not in kernel assert "GetEventRecord" not in kernel assert "session.sdmaSession" not in kernel - assert "COMPLETION_TYPE_SDMA_POST_DONE" in kernel + assert "COMPLETION_TYPE_SDMA_EVENT_RECORD" in kernel + assert "LoadSdmaSession" in kernel + assert "runtimeCtx.postDoneBase" in kernel assert "post_id" in kernel + assert "PTO2_ERROR" not in kernel - assert "COMPLETION_TYPE_SDMA_POST_DONE" in wait + assert "COMPLETION_TYPE_SDMA_EVENT_RECORD" in wait + assert "COMPLETION_TYPE_SDMA_POST_DONE" not in wait assert "backend_cookie" in wait - assert "poll_sdma_post_done" in wait + assert "poll_sdma_post_done_record(cond.addr, cond.backend_cookie)" in wait def test_a2a3_sdma_mailbox_carries_backend_cookie() -> None: @@ -32,4 +43,5 @@ def test_a2a3_sdma_mailbox_carries_backend_cookie() -> None: assert "uint64_t backend_cookie" in mailbox assert "backend_cookie" in mailbox and "try_push_condition(" in mailbox assert "backend_cookie" in types - assert "COMPLETION_TYPE_SDMA_POST_DONE" in types + assert "COMPLETION_TYPE_SDMA_EVENT_RECORD" in types + assert "COMPLETION_TYPE_SDMA_POST_DONE" not in types diff --git a/tests/ut/py/test_a2a3_sdma_workspace_source.py b/tests/ut/py/test_a2a3_sdma_workspace_source.py index f289c8a4a8..033b318bcd 100644 --- a/tests/ut/py/test_a2a3_sdma_workspace_source.py +++ b/tests/ut/py/test_a2a3_sdma_workspace_source.py @@ -1,8 +1,15 @@ +# Copyright (c) PyPTO Contributors. +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of +# CANN Open Software License Agreement Version 2.0 (the "License"). +# Please refer to the License for details. You may not use this file except in compliance with the License. +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. +# See LICENSE in the root of the software repository for the full text of the License. +# ----------------------------------------------------------------------------------------------------------- from __future__ import annotations from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[3] HOST_COMM = REPO_ROOT / "src/a2a3/platform/onboard/host/comm_hccl.cpp" HOST_CMAKE = REPO_ROOT / "src/a2a3/platform/onboard/host/CMakeLists.txt" @@ -14,23 +21,21 @@ def test_a2a3_host_uses_unified_sdma_workspace_interface() -> None: assert '#include "pto/comm/workspace.hpp"' in source assert "sdma_workspace_manager.hpp" not in source assert "std::unique_ptr" not in source - assert "pto::comm::Workspace sdma_workspace{}" in source + assert "auto workspace = std::make_unique()" in source assert "pto::comm::WorkspaceRequest req{}" in source - assert "pto::comm::CreateWorkspace(pto::comm::DmaEngine::SDMA, req, &h->sdma_workspace)" in source - assert "h->host_ctx.workSpace = reinterpret_cast(h->sdma_workspace.addr)" in source - assert "h->host_ctx.workSpaceSize = h->sdma_workspace.bytes" in source + assert "pto::comm::CreateWorkspace(pto::comm::DmaEngine::SDMA, req, workspace.get())" in source + assert "addr_out[DMA_WORKSPACE_SDMA] = reinterpret_cast(workspace->addr)" in source assert "16 * 1024" not in source def test_a2a3_sdma_workspace_release_paths_are_explicit() -> None: source = HOST_COMM.read_text() - assert "static void destroy_sdma_workspace(CommHandle h)" in source - assert "pto::comm::DestroyWorkspace(&h->sdma_workspace)" in source - assert "static void abandon_sdma_workspace(CommHandle h)" in source - assert source.count("pto::comm::AbandonWorkspace(&h->sdma_workspace)") >= 2 - assert "destroy_sdma_workspace(h);" in source - assert "abandon_sdma_workspace(h);" in source + assert "pto::comm::AbandonWorkspace(workspace.get())" in source + assert "std::unique_ptr workspace(static_cast(handle))" in source + assert "pto::comm::DestroyWorkspace(workspace.get())" in source + assert "destroy_sdma_workspace" not in source + assert "abandon_sdma_workspace" not in source def test_a2a3_host_cmake_maps_simpler_sdma_to_unified_workspace_macros() -> None: diff --git a/tests/ut/py/test_a5_sdma_workspace_source.py b/tests/ut/py/test_a5_sdma_workspace_source.py index 94b0a9732a..953741d245 100644 --- a/tests/ut/py/test_a5_sdma_workspace_source.py +++ b/tests/ut/py/test_a5_sdma_workspace_source.py @@ -1,8 +1,15 @@ +# Copyright (c) PyPTO Contributors. +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of +# CANN Open Software License Agreement Version 2.0 (the "License"). +# Please refer to the License for details. You may not use this file except in compliance with the License. +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. +# See LICENSE in the root of the software repository for the full text of the License. +# ----------------------------------------------------------------------------------------------------------- from __future__ import annotations from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[3] HOST_COMM = REPO_ROOT / "src/a5/platform/onboard/host/comm_hccl.cpp" HOST_CMAKE = REPO_ROOT / "src/a5/platform/onboard/host/CMakeLists.txt" @@ -13,6 +20,7 @@ def test_a5_sdma_overlay_uses_unified_workspace_interface() -> None: assert '#include "pto/comm/workspace.hpp"' in source assert "pto/comm/async/sdma/sdma_workspace_manager.hpp" not in source + assert "pto/comm/async/urma/urma_workspace_manager.hpp" not in source assert "std::unique_ptr" not in source assert "pto::comm::Workspace sdma_workspace{}" in source assert "pto::comm::WorkspaceRequest req{}" in source @@ -24,13 +32,15 @@ def test_a5_sdma_overlay_uses_unified_workspace_interface() -> None: def test_a5_sdma_release_and_domain_paths_use_workspace_object() -> None: source = HOST_COMM.read_text() - ensure_sdma = source[source.index("static void ensure_sdma_workspace") : source.index("#ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE", source.index("static void ensure_sdma_workspace"))] + start = source.index("static void ensure_sdma_workspace") + ensure_sdma = source[start : source.index("#ifdef SIMPLER_ENABLE_PTO_URMA_WORKSPACE", start)] assert "pto::comm::DestroyWorkspace(&h->sdma_workspace)" in source assert "pto::comm::AbandonWorkspace(&h->sdma_workspace)" in source assert "domain_workspace_addr = reinterpret_cast(h->sdma_workspace.addr)" in source assert "domain_workspace_size = h->sdma_workspace.bytes" in source assert "GetWorkspaceAddr()" not in ensure_sdma + assert "h->sdma_workspace->" not in ensure_sdma def test_a5_sdma_cmake_maps_to_unified_workspace_macros() -> None: @@ -42,10 +52,11 @@ def test_a5_sdma_cmake_maps_to_unified_workspace_macros() -> None: assert "PTO_COMM_WORKSPACE_RDMA_SUPPORTED=0" in cmake -def test_a5_sdma_workspace_smoke_expects_non_dense_derived_context_to_inherit_workspace() -> None: - test_source = (REPO_ROOT / "tests/ut/py/test_worker/test_platform_comm.py").read_text() +def test_a5_sdma_derived_context_inherits_workspace_outside_urma_mode() -> None: + source = HOST_COMM.read_text() + start = source.index('extern "C" int comm_derive_context') + derive_context = source[start : source.index("void *newDevMem", start)] - assert "sdma_overlay_enabled" in test_source - assert "SIMPLER_ENABLE_PTO_SDMA_WORKSPACE" in test_source - assert "non-dense derived SDMA workSpace" in test_source - assert "non-dense derived SDMA workSpaceSize" in test_source + assert "ctx.workSpace = h->host_ctx.workSpace" in derive_context + assert "ctx.workSpaceSize = h->host_ctx.workSpaceSize" in derive_context + assert "if (!rank_ids_are_dense_prefix(rank_ids, rank_count))" in derive_context diff --git a/tests/ut/py/test_pto_isa.py b/tests/ut/py/test_pto_isa.py index 2d89b6b165..cea9690890 100644 --- a/tests/ut/py/test_pto_isa.py +++ b/tests/ut/py/test_pto_isa.py @@ -65,12 +65,12 @@ def fake_run_git(args, cwd=None, timeout=30, check=False): assert pto_isa._clone(target, PIN_A, verbose=False) assert calls == [ - ["clone", "--no-checkout", "https://github.com/hw-native-sys/pto-isa.git", str(target)], + ["clone", "--no-checkout", "https://gitcode.com/wxwnnzdyd/pto-isa.git", str(target)], ["checkout", "--detach", "--force", PIN_A], ] -def test_clone_falls_back_to_gitcode_after_three_github_pin_failures(tmp_path, monkeypatch): +def test_clone_falls_back_to_github_after_three_gitcode_pin_failures(tmp_path, monkeypatch): target = tmp_path / "build" / "pto-isa" clone_remotes = [] land_results = iter([False, False, False, True]) @@ -89,15 +89,15 @@ def fake_run_git(args, cwd=None, timeout=30, check=False): assert pto_isa._clone(target, PIN_A, verbose=False) assert clone_remotes == [ + "https://gitcode.com/wxwnnzdyd/pto-isa.git", + "https://gitcode.com/wxwnnzdyd/pto-isa.git", + "https://gitcode.com/wxwnnzdyd/pto-isa.git", "https://github.com/hw-native-sys/pto-isa.git", - "https://github.com/hw-native-sys/pto-isa.git", - "https://github.com/hw-native-sys/pto-isa.git", - "https://gitcode.com/luohuan40/pto-isa.git", ] assert sleeps == [2, 4] -def test_clone_falls_back_to_gitcode_after_three_github_clone_failures(tmp_path, monkeypatch): +def test_clone_falls_back_to_github_after_three_gitcode_clone_failures(tmp_path, monkeypatch): target = tmp_path / "build" / "pto-isa" clone_remotes = [] @@ -108,7 +108,7 @@ def fake_run_git(args, cwd=None, timeout=30, check=False): assert args[:2] == ["clone", "--no-checkout"] remote = args[2] clone_remotes.append(remote) - returncode = 1 if remote == "https://github.com/hw-native-sys/pto-isa.git" else 0 + returncode = 1 if remote == "https://gitcode.com/wxwnnzdyd/pto-isa.git" else 0 return subprocess.CompletedProcess(["git", *args], returncode=returncode, stdout="", stderr="unavailable") monkeypatch.setattr(pto_isa, "_run_git", fake_run_git) @@ -116,10 +116,10 @@ def fake_run_git(args, cwd=None, timeout=30, check=False): assert pto_isa._clone(target, PIN_A, verbose=False) assert clone_remotes == [ + "https://gitcode.com/wxwnnzdyd/pto-isa.git", + "https://gitcode.com/wxwnnzdyd/pto-isa.git", + "https://gitcode.com/wxwnnzdyd/pto-isa.git", "https://github.com/hw-native-sys/pto-isa.git", - "https://github.com/hw-native-sys/pto-isa.git", - "https://github.com/hw-native-sys/pto-isa.git", - "https://gitcode.com/luohuan40/pto-isa.git", ] diff --git a/tests/ut/py/test_runtime_builder.py b/tests/ut/py/test_runtime_builder.py index 8846da3c91..c942795c50 100644 --- a/tests/ut/py/test_runtime_builder.py +++ b/tests/ut/py/test_runtime_builder.py @@ -714,7 +714,7 @@ def test_a2a3_onboard_folds_in_pto_isa_commit(self, monkeypatch): monkeypatch.setattr(pto_isa, "read_pto_isa_pin", lambda: "isa_sha") builder = self._make_builder("a2a3") - assert builder._build_cache_stamp() == "runtime_sha:pto-isa=isa_sha" + assert builder._build_cache_stamp() == "runtime_sha:pto-isa=isa_sha:ascend-home=" def test_a5_default_folds_in_pto_isa_commit(self, monkeypatch): """a5 default SDMA workspace folds the pto-isa pin into the cache stamp.""" @@ -726,7 +726,7 @@ def test_a5_default_folds_in_pto_isa_commit(self, monkeypatch): monkeypatch.setattr(pto_isa, "read_pto_isa_pin", lambda: "isa_sha") builder = self._make_builder("a5") - assert builder._build_cache_stamp() == "runtime_sha:pto-isa=isa_sha" + assert builder._build_cache_stamp() == "runtime_sha:pto-isa=isa_sha:ascend-home=" def test_non_a2a3_onboard_uses_pure_runtime_sha(self, monkeypatch): """Other arch/variant ignores pto-isa → stamp keyed on runtime HEAD only."""