Skip to content

Fix: honor ASCEND_RT_VISIBLE_DEVICES at comm_hccl MemSetAccess sites - #1826

Merged
ChaoZheng109 merged 2 commits into
hw-native-sys:mainfrom
lterrac:fix/comm-hccl-memsetaccess-vis-devices
Aug 19, 2026
Merged

ChaoZheng109 merged 2 commits into
hw-native-sys:mainfrom
lterrac:fix/comm-hccl-memsetaccess-vis-devices

Conversation

@lterrac

@lterrac lterrac commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Under ASCEND_RT_VISIBLE_DEVICES isolation, comm_alloc_domain_windows fails on every card pair except an identity mapping:

domain_alloc_via_fabric: [comm_hccl.cpp:1165] [comm rank 0] alloc_domain: create local Fabric window -> 507899

507899 is ACL_ERROR_RT_DRV_INTERNAL_ERROR — the driver was handed a device id in a numbering it does not use. Cards 0,1 hide it because there logical == physical, which is also why serving has never hit it: DeepSeek V4 takes all 8 cards.

This is the class #1600 fixed, at a site it did not cover — neither arch's comm_hccl.cpp included common/acl_hal_device.h.

Root cause — the two location.id fields want different id spaces

aclrtMemAccessDesc::location.id is consumed in the driver-visible space and must translate, even though aclrtMemSetAccess is an ACL entry point. Its sibling aclrtPhysicalMemProp::location.id must not — aclrtMallocPhysical rejects a driver-visible id outright.

Measured by walking the window sequence one call at a time, in one process. The grant matters: the translated id of the bound device has to fall outside the logical range, or the same value is legal in both spaces and no outcome can tell them apart — an earlier revision of this section probed 1,2, where it cannot. Under ASCEND_RT_VISIBLE_DEVICES=2,3 bound to logical 0 (= card 2), logical space [0,1]:

aclrtMemGetAllocationGranularity  prop.location.id=0 (bound logical) -> 0        OK
aclrtMallocPhysical               prop.location.id=0 (bound logical) -> 0        OK, 2 GiB appears on card 2
aclrtMemGetAllocationGranularity  prop.location.id=2 (translated)    -> 107001   rejected
aclrtMallocPhysical               prop.location.id=2 (translated)    -> 107001   rejected
aclrtMallocPhysical               prop.location.id=1 (other logical) -> 507899   rejected
aclrtReserveMemAddress                                               -> 0        OK
aclrtMapMem                                                          -> 0        OK
aclrtMemSetAccess                 desc.location.id=0 (logical)       -> 507899   FAILS
aclrtMemSetAccess                 desc.location.id=2 (translated)    -> 0        OK

107001 is ACL_ERROR_RT_INVALID_DEVICEID, and that row is what fixes the field's id space: under a driver-visible reading, 2 is the bound card and the call would have succeeded. The card the memory lands on was read off npu-smi's HBM column during a 2 GiB hold, not inferred.

With the variable unset and the card bound directly, the whole sequence completes — measured on card 1 when this was first traced, and identical for ACL_HBM_MEM_HUGE (fabric) and ACL_HBM_MEM_NORMAL (ipc). Today's re-measurement above covers ACL_HBM_MEM_HUGE only; the id spaces are a property of the fields, not of memAttr.

The asymmetry is per-field, not per-API, so acl_hal_device.h now records both halves. A symmetric "fix" of the memory property fails loudly rather than silently allocating elsewhere — 107001 for the translated id, 507899 for any other logical id — because the field must name the bound logical device, not merely a logical one.

Change

Three sites carry the id into aclrtMemSetAccess:

  • a2a3 reserve_and_map_vmm_window — the funnel for every local and imported window, fabric and IPC
  • a5 alloc_windows_via_ipc and domain_alloc_via_ipc — their descriptors are reused for the peer mappings, so the translation rides along

No CMake change: both onboard host targets already carry src/common/platform/include.

Test

tests/st/vis_isolation gains a comm-domain case next to the chip-init one, so both device-id layers live in one module. Chip init never maps device memory, so it cannot catch this. The new case runs on all granted cards rather than a single logical id, and reuses the existing ascending-list rule and skip-on-identity guard — under an identity grant the two id spaces hold the same number and the test would pass without proving anything.

Verification

a2a3, on 910B2 — A/B on the same granted cards in the same job, only the runtime differing:

Cards Mapping Stock This change
visible 1,2, logical 0,1 non-identity both ranks abort, create local Fabric window -> 507899 both complete byte-exact
visible 0,1, logical 0,1 identity passes passes (no regression)

The identity case matters: the broker hands out the lowest free cards, so a two-card request usually lands on exactly the mapping that proves nothing.

a5 — no a5 hardware available to me; it rides on _st-npu-a5.yml. The change there is the same pattern as a2a3, applied to the two inlined copies.

Related, not fixed here

The peer-access-status confusion already recorded in comments at a5 comm_hccl.cpp:553-561 and :972-977 (#1018) is the same id-space split at a different call (aclrtDevicePeerAccessStatus / aclrtDeviceEnablePeerAccess take a peer id). Left untouched rather than widening this diff.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7428be89-a8df-4603-b240-3486eaf4642c

📥 Commits

Reviewing files that changed from the base of the PR and between 300fb6d and 1c2b5ba.

📒 Files selected for processing (4)
  • src/a2a3/platform/onboard/host/comm_hccl.cpp
  • src/a5/platform/onboard/host/comm_hccl.cpp
  • src/common/platform/include/common/acl_hal_device.h
  • tests/st/vis_isolation/test_vis_isolation.py

📝 Walkthrough

Walkthrough

VMM access descriptors now convert ACL-logical device IDs to driver-visible IDs before memory access setup. Documentation records the field-specific ID rules. Visibility-isolation tests validate communication-domain mappings across remapped logical ranks.

Changes

VMM device ID translation

Layer / File(s) Summary
Translate VMM access descriptors
src/common/platform/include/common/acl_hal_device.h, src/a2a3/platform/onboard/host/comm_hccl.cpp, src/a5/platform/onboard/host/comm_hccl.cpp
VMM access descriptors use pto::acl_to_hal_device_id. Documentation distinguishes driver-visible access descriptor IDs from logical physical-memory property IDs.
Validate remapped communication domains
tests/st/vis_isolation/test_vis_isolation.py
The tests validate visible-device remapping and run communication-domain async-notify scenes across remapped logical ranks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to 1c2b5

This change translates device identifiers for isolated-device memory access while preserving existing behavior for identity mappings; no actionable merge-blocking risk remains beyond normal checks and review.

Poem

A rabbit checked each device ID,
From logical paths to drivers wide.
Domains hopped through visible space,
Tests ran fast from place to place.
“All mapped!” the bunny cried.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: applying visible-device mappings at comm_hccl MemSetAccess sites.
Description check ✅ Passed The description directly explains the failure, root cause, code changes, tests, and verification for the device-ID mapping fix.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Under `ASCEND_RT_VISIBLE_DEVICES` isolation a launcher renumbers the visible cards
to logical `0..N-1`. `comm_alloc_domain_windows` then fails on every card pair
except an identity mapping, aborting the domain with

    domain_alloc_via_fabric: create local Fabric window -> 507899

507899 is `ACL_ERROR_RT_DRV_INTERNAL_ERROR`: the driver was handed a device id in
a numbering it does not use.

`aclrtMemAccessDesc::location.id` is consumed in the driver-visible space and must
translate, even though `aclrtMemSetAccess` is an ACL entry point. Its sibling
`aclrtPhysicalMemProp::location.id` must NOT — `aclrtMallocPhysical` rejects a
driver-visible id outright. Walking the window sequence one call at a time under
`ASCEND_RT_VISIBLE_DEVICES=1,2`, bound to logical 0 (card 1):

    aclrtMemGetAllocationGranularity  prop.location.id=0 (logical)         -> 0
    aclrtMallocPhysical               prop.location.id=0 (logical)         -> 0
    aclrtMallocPhysical               prop.location.id=2 (driver-visible)  -> 107001
    aclrtReserveMemAddress                                                 -> 0
    aclrtMapMem                                                            -> 0
    aclrtMemSetAccess                 desc.location.id=0 (logical)         -> 507899
    aclrtMemSetAccess                 desc.location.id=1 (driver-visible)  -> 0

With the variable unset and card 1 bound directly the whole sequence completes.
Identical for `ACL_HBM_MEM_HUGE` (fabric) and `ACL_HBM_MEM_NORMAL` (ipc). The
asymmetry is per-field, not per-API, so `acl_hal_device.h` records both halves —
translating the memory property too would break allocation outright.

Three sites carry the id into `aclrtMemSetAccess`: a2a3's
`reserve_and_map_vmm_window`, the funnel for every local and imported window, and
a5's `alloc_windows_via_ipc` / `domain_alloc_via_ipc`, whose descriptors are
reused for their peer mappings.

The ST gains a comm-domain case beside the chip-init one, so both device-id layers
are covered: chip init never maps device memory, so it cannot catch this. It runs
on all granted cards rather than a single logical id, and keeps the existing
skip-on-identity guard — an identity grant makes the two id spaces the same number
and would pass without proving anything.

a2a3 verified on 910B2: with the stock runtime both ranks abort with 507899 on
cards 1,2; with this change they complete byte-exact, and an identity grant (cards
0,1) still passes. a5 has no hardware here and rides on CI.

Related: the peer-access-status confusion recorded at a5 comm_hccl.cpp:553-561 and
:972-977 (hw-native-sys#1018) is the same id-space split at a different call, left untouched.
@lterrac
lterrac force-pushed the fix/comm-hccl-memsetaccess-vis-devices branch from 1c2b5ba to a3a3b35 Compare August 14, 2026 09:20
Comment on lines +42 to +43
* aclrtMallocPhysical prop.location.id=0 -> 0 prop.location.id=2 -> 107001
* aclrtMemSetAccess desc.location.id=0 -> 507899 desc.location.id=1 -> 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The two rows in this table use different driver-visible ids for the same logical id, and I can't tell which one is the typo:

Under isolation, binding logical 0 of ASCEND_RT_VISIBLE_DEVICES=1,2:
  aclrtMallocPhysical  prop.location.id=0 -> 0       prop.location.id=2 -> 107001
  aclrtMemSetAccess    desc.location.id=0 -> 507899  desc.location.id=1 -> 0

For logical 0 under 1,2 the driver-visible id is 1, which is what the MemSetAccess row uses. The MallocPhysical row uses 2 — that is the driver-visible id of logical 1, and it is also out of range of the logical space [0,1]. So 107001 there is equally consistent with "invalid device id", which means that half of the row does not distinguish the two id spaces at all.

The left half does: prop.location.id=0 -> 0 succeeds even though card 0 is not in the granted set, which is real evidence that the field is logical. The conclusion looks right to me — it's specifically the second column I can't reconcile.

This matters more than a normal doc nit because this table is the only stated evidence for the per-field-not-per-API asymmetry, and it's what will stop the next reader from "fixing" prop.location.id symmetrically and silently allocating on the wrong card. Could you confirm whether 2 is what was actually measured, and if so which logical id it was bound to?

The same figure appears in the PR description.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, and to answer the question directly: yes, 2 is what was measured, bound to logical 0. So the row does not distinguish the two spaces — and it is a bit worse than ambiguous, because 107001 is literally ACL_ERROR_RT_INVALID_DEVICEID, which makes "invalid id" the natural reading rather than a competing one.

Under a 1,2 grant nothing can discriminate: the translated id of logical 0 is 1, and 1 is itself a valid logical id there, so every value is legal in both spaces.

Re-measured on ASCEND_RT_VISIBLE_DEVICES=2,3 bound to logical 0 (card 2), where the translated id of the bound device falls outside the logical range [0,1]:

aclrtMallocPhysical  prop.location.id=0 (bound logical) -> 0        2 GiB appears on card 2
                     prop.location.id=2 (translated)    -> 107001
                     prop.location.id=1 (other logical) -> 507899
aclrtMemSetAccess    desc.location.id=0 (logical)       -> 507899
                     desc.location.id=2 (translated)    -> 0

Now the 107001 row carries the argument: under a driver-visible reading, 2 is the bound card, so the call would have succeeded. aclrtMemGetAllocationGranularity rejects the same value identically, so the check is not specific to the allocation call, and the card the memory landed on was read off npu-smi's HBM column during a 2 GiB hold rather than inferred.

One thing your question made me test, and I had it wrong: I would have said a symmetric translation of the memory property allocates on the wrong card silently. It does not — 507899 for any other logical id — so it fails loudly. The header now says that instead, since "silent mis-allocation" would have been exactly the wrong warning to leave for the next reader.

Fixed in f6b511a, PR description included.

}

// aclrtMemAccessDesc::location.id is consumed in the driver-visible space, unlike
// aclrtPhysicalMemProp::location.id above, which takes the ACL-logical id. Under

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

"unlike aclrtPhysicalMemProp::location.id above" — in this file the prop is not above: it lives in create_local_vmm_window at line 301, which is this function's caller and is defined below it.

Looks like this block was copied from the a5 file, where the wording is accurate (prop at 460, desc at 492).

Suggest: "unlike aclrtPhysicalMemProp::location.id in its caller create_local_vmm_window, which takes the ACL-logical id".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct — copied from the a5 file, where prop (460) does precede desc (492). In this file it is in the caller create_local_vmm_window, defined below. Used your wording in f6b511a.

Review caught that the aclrtMallocPhysical row did not support the claim it was
cited for. It was taken under ASCEND_RT_VISIBLE_DEVICES=1,2 bound to logical 0,
and probed prop.location.id=2 — which is neither the translated id of the bound
device (that is 1) nor inside the logical range [0,1]. Since 107001 is literally
ACL_ERROR_RT_INVALID_DEVICEID, that rejection is equally explained by "invalid
id", so the row could not tell the two id spaces apart. Under that grant nothing
can: translated(logical 0) = 1 is itself a valid logical id, so every value is
legal in both spaces.

Re-measured under ASCEND_RT_VISIBLE_DEVICES=2,3 bound to logical 0 (card 2),
where the translated id of the bound device falls outside the logical range:

  aclrtMallocPhysical  prop.location.id=0 (bound logical) -> 0        2 GiB appears on card 2
                       prop.location.id=2 (translated)    -> 107001
                       prop.location.id=1 (other logical) -> 507899
  aclrtMemSetAccess    desc.location.id=0 (logical)       -> 507899
                       desc.location.id=2 (translated)    -> 0

The 107001 row now settles the question: under a driver-visible reading, 2 *is*
the bound card, so the call would have succeeded. It does not, and the same value
is rejected identically by aclrtMemGetAllocationGranularity, so the check is not
specific to the allocation call. The card the memory lands on was read off
npu-smi's HBM column during a 2 GiB hold, not inferred.

One prediction is withdrawn by the same measurement: a symmetric translation of
the memory property does not silently allocate on the wrong card. It fails
loudly — 107001 for the translated id, 507899 for any other logical id — because
the field must name the bound logical device, not merely a logical one.

Also corrects the a2a3 comment: aclrtPhysicalMemProp is not "above" in that file,
it is in the caller create_local_vmm_window, which is defined below. The wording
was copied from the a5 file, where prop does precede desc.

No functional change.
@ChaoZheng109
ChaoZheng109 merged commit 7fa3f54 into hw-native-sys:main Aug 19, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants