Skip to content

fix(ascend): stop A5 from receiving a ranktable its HCCL rejects - #27

Merged
thxCode merged 2 commits into
mainfrom
feat/ascend-device-injection-policy
Sep 6, 2026
Merged

fix(ascend): stop A5 from receiving a ranktable its HCCL rejects#27
thxCode merged 2 commits into
mainfrom
feat/ascend-device-injection-policy

Conversation

@thxCode

@thxCode thxCode commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Supersedes #25. @yxf0314's diagnosis was right that multi-card A5 needed a change — this takes the part of it that holds up on hardware, and deliberately drops the rest.

Summary

This is defence, not repair. With the host's stale ranktable moved aside, unmodified gpustack-runtime already runs GLM-5.3-Flash TP=8 under the default Env policy, serving real requests. Nothing is blocked today once the host file is right. What this PR stops is a node's leftover ranktable silently killing every multi-card workload on it, with an error that gives the operator nothing to go on.

The failure chain, measured on an 8x Ascend950PR host: ascend-docker-runtime mounts /etc/hccl_rootinfo.json whenever the host has it (addUBMount is a bare os.Stat, no version check). A5 loads libhccl_v2.so, which accepts a 2.0 ranktable only. A table left from an A2 fleet is 1.0, so HCCL refuses it with Config_Error_Ranktable(EI0014) and multi-card init never happens. Ranktable format is bound to the chip generation — 1.0 = A2, 1.2 = A3, 2.0 = A5 (ascend-operator/pkg/ranktable/common/common.go:37-38, ranktable/v2dot0/ranktable.go:27). Isolated: adding only that file to an otherwise-working container reproduces EI0014; adding only driver/topo does not. Nothing in libhccl_v2.so's HCCL_*/RANK_*/ASCEND_* strings disables the check, so not mounting the file is the only lever.

The file is user-maintained state — it belongs to no driver package, and the vendor documents it as generated by mindcluster-tools and mounted only when present. The operating rule is simply: either absent, or a 2.0 table.

Changes

  • GPUStack stops mounting the ranktable on A5. This is GPUStack's own mount list, so it covers the CDI path. Older generations keep the file, which is correct for them
  • A one-shot warning naming EI0014. On the default Env path the mount is the vendor runtime's and GPUStack cannot prevent it, so reporting is the only available action — the warning says the file must be absent or 2.0. An absent file is the healthy host and stays silent; so does an older generation with its own table, since only the A5 row of the generation mapping has a measured failure behind it

Deliberately not included

Both were built and measured, then dropped:

  • A Device injection policy (feat(ascend): add Device injection policy and fix A5 driver/UB mounts #25's main proposal). It adds a configuration knob that does nothing by default — and the default Env path is exactly where the vendor runtime does the mounting, so it would only protect operators who had already been told to configure it. Its isolation is also nominal in the shape GPUStack deploys in: a privileged container sees every device node whatever it was granted, and GPUStack deploys workloads privileged today
  • Mounting the whole /usr/local/Ascend/driver for A5 (feat(ascend): add Device injection policy and fix A5 driver/UB mounts #25's other change). driver/ube_mgmt/ is a staging directory for upgrade-tool --upgrade_ube_mgmt_pack holding a single zero-byte lock file; no library under driver/lib64/ references it, and ube_mgmt appears zero times in all of mind-cluster. The real UB code (libascend_hal.so, 15 urma symbols; libibv_extend.so*) is already inside the lib64 mount. The tree would also carry driver/tools/ (upgrade-tool, hccn_tool, both 0555, beside ~120 MB of firmware images) into a container that already holds /dev/davinci_manager, plus driver/cert/ca and 2570 DKMS kernel sources

Verification

  • uv run pytest: 651 passed, 20 skipped; pre-commit clean (ruff check, ruff format, codespell, commitizen)
  • 17 new tests covering the A5 mount profile (A5 omits the file, older generations keep it, the UB globs stay A5-only) and the warning (stale 1.0, A3 1.2, correct 2.0, absent file, non-A5 host, warns once, malformed JSON, and that the warning resolves the same path constant the mount list is built from)
  • Hardware evidence is from a live 8x Ascend950PR host (driver 25.7.rc1.6, ascend-docker-runtime v26.1.0, CANN 9.1). The end-to-end confirmation that GLM-5.3-Flash TP=8 serves once the host ranktable is corrected was taken against unmodified 0.2.4.post1, which is what establishes the defence-not-repair framing above

Not covered

That host is a card-4p inference node with no super-pod UB interconnect (spod-info unsupported, no /dev/uburma, no user-space urma libraries; card-internal UB endpoints do enumerate through dcmi). It therefore says nothing about #25's original claim that env-triggered isolation hides the UB fabric — that needs a super-pod-attached A5. EI0014 explains the observed failure completely without invoking UB.

The evidence is also single-node. A multi-node deployment does need a ranktable; when that lands it has to be a 2.0 one, and this is noted in the code.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new Device resource injection policy for the Docker deployer, allowing direct injection of device nodes and bind mounts without relying on a CDI-capable Docker or setting visible-devices environment variables. It also updates the Ascend CDI generator to omit the host ranktable on A5 (Ascend 950) hosts to prevent initialization failures, and adds a warning for incompatible ranktables. Feedback suggests calling the ranktable warning only under the Env policy since other policies already prevent mounting it, and defensively checking for the ranktable path's presence before removing it from the mount list.

Comment thread gpustack_runtime/deployer/docker.py Outdated
Comment thread gpustack_runtime/deployer/cdi/ascend.py
Supersedes #25. yxf0314's diagnosis was right that multi-card A5 needed a
change; this takes the part of it that holds up, and drops the rest.

A5 loads libhccl_v2.so, which accepts a 2.0 ranktable only. Ranktable format is
bound to the chip generation -- 1.0 for A2, 1.2 for A3, 2.0 for A5 -- so a table
left over from an older fleet is refused, not ignored: HCCL fails with
Config_Error_Ranktable(EI0014) and multi-card init never happens. Isolated on an
8x Ascend950PR host: mounting only /etc/hccl_rootinfo.json into an otherwise
working container reproduces EI0014, mounting only driver/topo does not.

Two things follow, and nothing else does.

GPUStack stops mounting the file on A5. This is its own mount list only, so it
covers the CDI path. Older generations keep the file, which is correct for them.

Under the default Env policy the mount is ascend-docker-runtime's -- addUBMount
is a bare os.Stat with no version check -- so GPUStack cannot prevent it and
reporting is the only available action. A one-shot warning names EI0014 and says
the file must be absent or 2.0. An absent file is the healthy host and stays
silent; so does an older generation with its own table, since only the A5 row of
the generation mapping has a measured failure behind it.

This is defence, not repair: with the host file moved aside, unmodified
gpustack-runtime already runs GLM-5.3-Flash TP=8 under the default policy.
What it stops is a node's leftover ranktable silently killing multi-card
workloads on it.

Deliberately not included, having been measured and found unnecessary:

  A Device injection policy. It adds a configuration knob that does nothing by
  default, on the Env path where the vendor runtime does the mounting -- so it
  would not protect anyone who had not already been told to configure it. Its
  isolation is also nominal in the shape GPUStack deploys in, since a privileged
  container sees every device node whatever it was granted.

  Mounting the whole /usr/local/Ascend/driver for A5. driver/ube_mgmt is a
  staging directory for upgrade-tool holding one zero-byte lock file; no library
  under driver/lib64 references it, and the urma symbols are already inside the
  lib64 mount. The tree would also carry upgrade-tool and hccn_tool into a
  container holding /dev/davinci_manager, plus the CA store and 2570 kernel
  sources.
@thxCode
thxCode force-pushed the feat/ascend-device-injection-policy branch from 4c92a8b to 535e7a4 Compare September 6, 2026 13:55
@thxCode thxCode changed the title feat(ascend): add Device injection policy, correct the A5 mount profile fix(ascend): stop A5 from receiving a ranktable its HCCL rejects Sep 6, 2026
@thxCode
thxCode merged commit 55bbd00 into main Sep 6, 2026
7 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.

1 participant