Add: TMR kernel invocation snapshots and native transport adapter(K4) - #2180
Draft
Leaf-Salix wants to merge 5 commits into
Draft
Add: TMR kernel invocation snapshots and native transport adapter(K4)#2180Leaf-Salix wants to merge 5 commits into
Leaf-Salix wants to merge 5 commits into
Conversation
Kernel mode is simpler's second execution identity: instead of owning
the device, a context borrows the caller's already-current device and
stream to enqueue one bounded asynchronous operator per launch, so a
PyPTO program is capturable by ACLGraph as an ordinary node. This
change freezes the public surface that identity hangs off and gives the
context a write-once identity the guards can key on. It creates no
resources. The program path gains one call — simpler_init latches
PROGRAM — and no behavior: latching a fresh context always succeeds, is
idempotent, and nothing on that path reads the latch.
- runtime_c_api.h declares the lifecycle entries
simpler_kernel_mode_{supported,init,prepare_callable,launch} and adds
the host-band code PTO_RUNTIME_ERR_INVALID_STATE. The existing
finalize_device stays the fifth lifecycle entry, and a kernel context
now reaches it. Kernel-mode capacity is
a mode invariant rather than a gated state: config is context-static,
so each pooled arena region is committed at most once, and
setup_static_arena reports a grow or release request on a committed
region under kernel mode as an internal invariant break; capacity
intent travels in CallConfig.runtime_env like everywhere else.
- Execution identity is a write-once property of the context rather
than a state that evolves. ExecutionModeLatch (platform/include/host/
execution_mode_latch.h) replaces the four-state claim: the first init
entry to run latches the mode, and it never changes — not on finalize,
not on error. simpler_init latches PROGRAM before touching any
process or runner state, so the program/kernel mutual exclusion is
enforced on every program init instead of resting on a separate
declaration call. There is no unlatch, which the latch documents as a
consequence: a handle from a failed kernel init can never be recycled
into a program context. SimplerExecutionMode now has one definition
(task_interface/execution_mode.h) that both the wire header and the
latch consume, so the host-side identity and the value that travels to
the AICPU can no longer disagree.
- device_id_ records which device a context is on, not a claim on it —
ownership is what the latch carries. attach_current_thread splits
accordingly: bind_current_thread does the per-thread rtSetDevice and
nothing else; attach_current_thread is the program-mode adopt
(bind plus the one-shot op-execute watchdog and identity write) and
refuses on a kernel latch; adopt_borrowed_device records the device a
kernel context runs on without binding the thread and without
configure_aicore_op_timeout, whose aclrtSetOpExecuteTimeOutV2 would
rewrite the watchdog for every other user of a borrowed card. It does
resolve the timeout config, because the stream and scheduler timeouts
derived from it are read on both identities. DeviceRunner::finalize()
is the one caller that runs under both identities and skips the bind
on a kernel latch, so the kernel close path reaches its no-reset
branch instead of being turned away by a device bind it never needed.
- ensure_acl_ready(), force_reset_device(), and finalize()'s rt-layer
device reset refuse on a kernel-mode context (a2a3 + a5): the ACL
lifecycle belongs to the caller, and every call site of the five ACL
lifecycle APIs falls into three enumerable classes (below the
ensure_acl_ready guard, inside force_reset_device behind its own
guard, or gated on acl_ready_ which only the guarded path sets), with
finalize's rt-layer reset intercepted by its own kernel-mode branch —
so poison recovery can never reset the device out from under the
host process.
- kernel_invocation_header.h pins the envelope every kernel launch
ships to the AICPU (mode / callable / generation / payload length /
int32_t arg counts). Both sides of the wire come from one
build_runtimes.py build, so the struct carries no version or size
negotiation and the POD/standard-layout guards are its only
compile-time checks. generation is the occupancy counter of the
residency slot callable_id resolves to - a property of the slot, not
of the callable in it, so a generation carried by the callable could
not detect slot reuse - with zero reserved for "not recorded".
ChipCallable's sig_count includes the scalar entries and its
scalar_count reads 0 both for a scalar-free orchestration and for an
artifact built before the field existed, so a consumer derives the
effective scalar count - the field when nonzero, otherwise the
signature's SCALAR entries, the split count_callable_tensor_args
already computes - and compares tensor_count against sig_count minus
it. Subtracting the field directly would count an unrecorded
callable's scalars as tensors.
- Kernel-entry argument validation is shared by all eight host-runtime
components through kernel_entry_validation.h (one copy of the
null/range/image-size/alignment checks; a binary pointer and its size
must be present or absent together, and a callable image must be
aligned for ChipCallable so its CALLABLE_CHILD_ALIGN-relative storage_
lands aligned too), so a stub and a real implementation accept and
reject exactly the same arguments.
- KernelExecutionState and ExecutionModeClaimState carry the kernel
context phase machine (New/Collecting/ReadyEnqueued/Poisoned/
Closing/Closed with sticky, retriable Closing and separate
runtime-error and teardown-error slots) and the two restricted
operation vocabularies; synchronize, allocation, capture queries,
and model attachment stay unrepresentable in those tables, and a
launch implementation is obligated to route through them. Every
kernel-mode guard reads the identity through ExecutionModeLatch::
is_kernel() rather than comparing an enumerator at the call site, so
the test lives in one place instead of eight.
- ChipWorker dlsyms the four new symbols from every runtime, so a
component missing one fails at load, and clears them alongside the
other resolved pointers on all three teardown paths so none is left
dangling into the library DlHandleGuard dlcloses.
test_host_runtime_abi.py
asserts the export across all eight components, and table-driven UTs
cover the phase machine (including failed-rollback landing in
Closing with the create error reported and the cleanup error
latched), the shared argument validation, and the wire layout.
- ChipCallable additionally records scalar_count as a cached
derivation of the signature's SCALAR entries: make_callable rejects
a nonzero count that disagrees with the signature, while 0 also
means "not recorded" (legacy blobs read 0). The field occupies four
bytes of historical header tail padding, so every historical offset,
sizeof, and the kernel-cache ABI token are unchanged;
ChipCallable.build gains a trailing scalar_count=0 keyword and a
read-only property.
Two facts a reader should not have to re-derive. The latch refusal returns
PTO_RUNTIME_ERR_INVALID_STATE (-1003) rather than PTO_RUNTIME_ERR_INTERNAL
(-1000) on purpose: conftest.py scrapes "simpler_init failed with code <N>"
and treats -1000 as a poisoned card, so an identity conflict must not look
like one. And kernel_execution_state.cpp stays compiled into all four host
runtimes even though grepping KernelExecutionState now finds only its own
header and .cpp — it is the persistent-state change's foundation, not an
orphaned translation unit.
Every kernel-mode branch this adds is provably dead in this commit: no
production site latches KERNEL (`git grep 'latch(SIMPLER_MODE_KERNEL)' src`
is empty) because both simpler_kernel_mode_init stubs return before any latch
call, so is_kernel() is false on every context and the program path takes the
same branch it took before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Compute TMR kernel arena requirements from the existing per-architecture sizing and reserve-only layout without acquiring device resources. Keep candidates call-local and publish output only on success. Share mode-aware contract and topology validation while keeping the TMR resource set explicit. Preserve valid program behavior and reject unserviceable stream declarations before context creation. Define execution modes in a neutral header shared with the unchanged invocation envelope. Connect the internal builder to validating kernel init stubs; HBG remains unsupported and kernel execution stays disabled. Cover sizing bounds, packed input, independent concurrent calls, real loader admission, sim entry behavior, and C/C++ header compatibility. Relevant tests and sim partitions pass. Full CTest retains the existing profiler quiesce failure (139/140); onboard validation remains pending.
Use the upstream execution-mode header as the single definition. Keep resource contracts independent of kernel lifecycle and invocation headers, and verify that boundary through compiler dependencies. Leave invocation layout ownership and its original wire tests with K1; remove the resource-contract test that additionally pins wire offsets. TMR sizing, resource validation, and init preflight remain unchanged.
Separate common invocation admission from the TMR wire codec. Encode independently owned packets with transactional host templates; consume trusted callable and execution-binding views without owning runtime resources or changing program-mode execution. Add bounded decoding, caller-owned argument conversion, unit tests, and an isolated CPU transport snapshot probe. Production kernel launch remains disabled until resource providers and execution are integrated.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Integrate persistent context resources from K2 commit 48b120b, including its four-argument prepare ABI and borrowed-device lifecycle. Preserve resource-contract admission before context mutation. Validate callable signatures and canonical storage before registration. Provide runner-backed CPU transport and trusted-view TMR consumption without enabling public launch or fabricating resource providers. Cover prepare rejection, consumption transactions and bounded templates. Fix shared buffer-pool lint without changing release-once semantics. Co-authored-by: YunjiQin <a1339924773@gmail.com>
4 tasks
This was referenced Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
TMR (
tensormap_and_ringbuffer) kernel mode needs a per-invocation argument snapshot that remains independent of mutable Host inputs and later calls. Reusing program bind/staging or overwriting a shared Runtime to pass each invocation would couple parameter lifetime to execution resources and make asynchronous submission difficult to reason about.This PR provides shared invocation validation → TMR snapshot encoding/decoding → a native CPU transport adapter, with explicit interfaces for trusted resource metadata:
This is K4 foundation plus K2 integration, not the complete kernel execution path. Onboard TMR init/prepare/close can exercise K2 resources, and real prepare consumes the shared callable admission checks. Sim init retains
UNSUPPORTEDafter contract validation; HBG kernel init remains unsupported without 2b.simpler_kernel_mode_supported()remains0, and public launch remains fail-closed rather than reaching the adapter. Stable execution-binding and residency-generation providers, the production device dispatch, and the complete submission binder remain dependencies. The included transport probe uses explicitly test-only metadata.Invocation contract and adaptation
Shared validation without a shared runtime payload
src/common/task_interface/kernel_invocation_validation.hprovides:PreparedInvocationView: trusted callable ID, effective tensor/scalar counts, and callable residency generation.derive_invocation_counts(): validates the signature and derives effective counts. A nonzero cached scalar count must agree with the signature; zero uses the signature-derived value.validate_invocation_header(): checks the formal K9 framing, kernel mode, callable ID range, count limits, payload length against the supplied readable span, and identity/count agreement with the trusted callable.host_copy_tensor_count != 0is rejected.These checks do not interpret TMR payload contents. HBG can reuse the common admission rules while retaining its own graph/blob validation. A trusted callable view must come from an independently validated, live registration; constructing it from the packet being checked would not establish trust.
No public C query or lifecycle API is added. The combined branch adopts K2's four-argument
simpler_kernel_mode_prepare_callable(ctx, cid, callable, callable_size)signature; prepare no longer receives a caller stream. This is an intentional prerequisite ABI alignment from the original K4 baseline, not a claim that all old signatures remain unchanged. Execution-mode definitions and formal K9 layout remain unchanged.src/common/platform/include/host/kernel_entry_validation.hconsumes the shared effective-count rules in real onboard/sim prepare admission. It also checks canonical callable names, child counts, alignment, offsets and complete variable-length storage bounds before hashing, registration or state mutation. These checks are kernel-prepare-only; program registration does not acquire this new validation path. Combined onboard init preserves 2a's builder/contract admission before mode latch, executor replacement or context resource creation.TMR-specific wire and consumption
src/common/tensormap_and_ringbuffer/kernel_invocation.hdefines the runtime-specific binding reference and bounded decoder. The packet layout is:The packet length is derived from the formal types and trusted effective counts:
The inherited K9 is currently 40 bytes;
TmrBindingRefis 16 bytes, with size/offset assertions. Packets contain descriptors and scalar bits, not Tensor data, callable binaries, Host pointers, STL objects, or an entire Runtime. No global maximum-sized Host packet is allocated.The two version fields protect different resources:
generation/PreparedInvocationView::slot_generationTmrBindingRef::context_generationdevice_binding_addris opaque to the codec. The provider owns the referenced binding's format, capacity validation, publication, and lifetime. A nonzero address or a matching generation does not independently prove that resources exist or remain alive.Encoding normalizes Tensor fields, excludes padding and unused dimensions from identity, and accepts only Device Tensor descriptors. Nonempty tensors undergo extent/backing and arithmetic checks. Empty tensors follow the existing boundary-conversion behavior; this is not a promise that arbitrary operators support empty inputs.
Decoding uses a pointer-plus-length
ByteSpan, validates exact TMR size and trusted bindings, and reads unaligned input through bounded copies. Failure leaves the previous output unchanged. The resulting view borrows immutable packet storage.kernel_invocation_args.hprovidesconsume_tmr_invocation(packet, trusted_callable, trusted_binding, out): decode and validate before materializing into consumer-ownedEntryArgsStorage, preservingouton rejection. It is an explicit consumption interface, not a registered production AICPU dispatch. It does not call programRuntime::set_orch_args()or overwrite dynamic arguments in a persistent Runtime. Storage must remain alive through the last config/orchestration access and every derived Tensor reference.Host templates and native transport
src/common/worker/tmr_kernel_invocation.hprovides one Host encoding-cache object per prepared callable and independently owned candidates:src/common/platform/onboard/host/tmr_kernel_invocation.{h,cpp}providesenqueue_tmr_invocation_aicpu(DeviceRunnerBase&, ...). It revalidates the candidate's owned length and trusted metadata, then calls the initialized runner'slaunch_aicpu_payload()CPU transport. The implementation is compiled into both architecture-specific onboard targets. The directLoadAicpuOpoverload remains available for the isolated transport probe. The owner supplies the dedicated AICPU stream and establishes the complete event ordering outside this adapter; linking the adapter does not wire public launch or install a device consumer.The adapter targets the kernel-specific
simpler_aicpu_kernel_execsymbol, not the existing programKernelArgs*entry. The production symbol/consumer is not registered by this PR; it exists in the isolated probe for transport validation.This does not require TMR and HBG to use the same SDK launch API. The common requirement is task-owned argument-copy semantics with a valid Host buffer until the native copy returns. The TMR probe exercises the existing
rtsLaunchCpuKernelpath, not an assumedWithHostArgsreplacement.Lifecycle and program-path impact
Submission transaction: The owner must protect the entire sequence, not just individual accessor calls:
The same protection must coordinate with prepare replacement and close. K4 introduces no independent cache mutex or parallel resource registry. Independent callers may use independent caches/outputs; shared-cache access requires owner serialization.
Ownership: K4 owns the Host packet until the native API completes its copy. Native success means enqueue, not device completion. CANN owns the task/graph argument copy; the resource providers keep callable and stable execution resources alive. Generation validation does not replace lifetime management. Partial enqueue failure and already-submitted work require binder error handling; leaving the Host cache unchanged does not cancel device work.
Alignment with other modules: The K2 stream/persistent-argument owner is included, not duplicated. Its arbitrary-payload transport is the runner adapter's integration point. The included K2 snapshot owns a private AICPU stream, a hidden AICore stream and five context events; this must not be described as proof of two-hidden-stream capture. K3/K5/K10 must provide actual trusted views; K4 does not substitute fixed generations, fake addresses, or the program Runtime. No cache sidecar or second registry is attached to context/callable state before a real owner consumes it.
Program-path impact: K4 does not redirect program bind, staging, Runtime upload or per-run cleanup through invocation snapshots. However, the combined diff includes K2 changes in shared platform, device-runner and worker code, so it is not accurate to say no existing production source is modified. It also includes two shared buffer-pool lint fixes: remove unspecified pointer-address ordering while retaining collect/deduplicate-before-release behavior, and use a single-mutex
scoped_lockwith the same notify lifetime. A regression test covers release-once and queue clearing; no warning suppression is added.Left to follow-up PRs: Trusted Host/device provider publication; capacity establishment and callable residency; complete stream/event submission and cancellation; production device dispatch; Torch/taskQueue/storage retention; ACLGraph capture/replay; and close/graph quiescence. Host concurrency tests do not establish safety for device execution, replay, or close races.
Tests
Evidence is separated below between the original
3e822453foundation and the combined K2/K4 source snapshot. Neither is a claim that the full CI matrix or the complete kernel pipeline has passed.Coverage added in this PR
tests/ut/cpp/types/test_kernel_invocation_validation.cpp: 3 cases covering effective scalar rules, invalid signatures/counts/modes/IDs/generations, unaligned framing, output preservation, and acceptance of opaque non-TMR payloads with independently trusted counts.tests/ut/cpp/common/test_tmr_kernel_invocation.cpp: 11 cases covering round-trip conversion, cache identity and dynamic patching, unchanged cache/output on failure, canonical padding, stale bindings, short/corrupt packets, self-consistent K9 lengths that still violate exact TMR size, tensor arithmetic/Device-only rules, empty boundary conversion, minimum/maximum/scalar-only packets, and independent/owner-serialized concurrent calls.tools/cann-examples/tmr-invocation-snapshot/: A committed, standalone Host/device probe using the actual CPU loader and packet layout. It submits 24 asynchronous packets, overwrites and releases Host packet storage, and verifies copied values only after a test-only device-read gate is released. Result buffers start with failure sentinels. The gate has a timeout and is not part of production execution.The combined update adds prepare signature/FAM rejection cases, real sim prepare entry coverage, trusted-view consumption/output-preservation cases, and 10,000 repeated encodes showing bounded logical template bytes. The latter is not an RSS, peak-heap or CANN task-memory measurement. It also includes K2's lifecycle tests and a buffer-pool release regression.
Original foundation results (
3e822453)a2a3simanda5sim: 4 passed on hwServer0The hwServer run used a fixed archive of the pushed commit in a new isolated directory. Archive hashes matched; post-test comparison found no source-content differences. Python/native imports were verified against this snapshot. Existing Torch dependencies were reused read-only; no Torch installation or shared-environment modification was performed.
Combined K2/K4 results (2026-09-10)
.cpp; no bypass or suppressionThe remote combined source archive SHA-256 is
e9b0fe33f0ae5fc4bef96124384ffdb6ca3959289f298056d790d9333812c1b2. That archive predates the final buffer-pool lint-only fix, which has separate local rebuild/test/sanitizer evidence. The original 24-packet probe was not rerun on this combined snapshot. K2 lifecycle success is not K4 invocation execution or capture/replay evidence. No Torch installation was performed.The old #2180 network1 failure at
3e822453was investigated separately: the CI daemon could not bind its occupied port, and tests reached a different daemon. This was not a K4 regression; no CI/daemon changes or rerun were made for that incident. The combined branch's future CI result is not inferred from that diagnosis.The native probe is eager CPU transport evidence, not formal TMR executor, A5 hardware, or ACLGraph capture/replay evidence. Test-only callable/binding metadata does not establish production provider correctness. Program smoke does not establish kernel-mode execution support. Local and remote runs of the same tests are not summed as unique cases.
Main reproduction commands, after isolated installation and building the targets according to the repository testing guide:
Not validated or not implemented: Full UT/ST suites, A5 hardware execution, empty-Tensor program execution against the baseline, production metadata/binding providers and device consumption, capture/replay, alternating captured graphs, asynchronous cancellation, and close/resource-reclamation races.
Commits
The branch retains the original foundation and adds the combined integration on top of #2177:
3e822453—Add: TMR kernel invocation snapshots and transport adapter: Shared validation, TMR wire/codec, transactional Host templates, native adapter, argument conversion, unit tests, and the standalone transport probe.b0943525—Add: integrate TMR snapshots with kernel context resources: K248b120b15resource lifecycle, 2a admission-preserving init integration, shared kernel prepare validation, runner transport adapter, explicit TMR consumption, regression tests and shared-header lint fixes. K2 human authorship is preserved in the commit trailer.Prerequisite order: #2064 → {#2177 (2a), #2176 (K2)} → this K4 PR. This is a dependency set, not a requirement that 2a and K2 merge in a specific order relative to each other. Later execution integration must consume the remaining resource/provider/binder modules; this PR neither replaces them nor declares their work complete.
中文总结
2153406a,保留3e822453,叠加 K248b120b15并完成组合接线;等待 Add: kernel-mode C ABI skeleton and wire headers (K1) #2064、Add: TMR kernel-mode resource contracts and init admission(2a) #2177、Add: persistent kernel-context execution resources (K2) #2176 合并后对齐复验,保持 Draft。0。onboard TMR init/prepare/close 可测试,不再笼统描述所有 init 都 unsupported。