[Bugfix][KV Connector][NIXL] Transfer the whole PLE short-conv page in disaggregated P/D by aijanai · Pull Request #59997 · vllm-project/vllm · GitHub
Skip to content

[Bugfix][KV Connector][NIXL] Transfer the whole PLE short-conv page in disaggregated P/D - #59997

Merged
vadiklyutiy merged 5 commits into
vllm-project:mainfrom
aijanai:amassidda/fix-ple-conv-state-pd
Oct 8, 2026
Merged

vadiklyutiy merged 5 commits into
vllm-project:mainfrom
aijanai:amassidda/fix-ple-conv-state-pd

Conversation

@aijanai

@aijanai aijanai commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fixes #59642 (Qwen3.8-Flash-Next: 0% MTP acceptance in disaggregated P/D serving with NixlConnector).

The NIXL worker folds every KV cache layer that reports the same base address into one region (register_kv_caches, if base_addr in seen_base_addresses). In the block-outer HMA layout every cache group places its first page at byte 0 of the shared allocation, so on Qwen3.8-Flash-Next the PLE short-conv state (Qwen4ExpPLELayer, a tp-replicated MambaSpec, page = channels × (conv_state_len + num_spec_tokens) = 245760 B) lands in the same region as layer-0 GDN, the layer-3 QSA ring and the layer-3 MLA indexer cache, and that region keeps the MLA page length (204800 B). The PLE descriptors built from block_len_per_layer[_ple_region_index] therefore transfer only 204800/245760 bytes of each PLE slot: 8534 of 10240 channels arrive on the decode worker, the rest is whatever the block held before (never zeroed: the block is exempt from zeroing because an async load is expected to overwrite it). With num_speculative_tokens >= 2 the spec-decode short conv reads those stale columns, the layer-1 hidden state becomes NaN, the target logits are NaN and the rejection sampler rejects every draft (the generated tokens are garbage too, not just slow).

This PR records the PLE page length at registration (_ple_block_len) and uses it for the PLE descriptors on both sides of the transfer (_build_mamba_local / _build_mamba_remote), exchanging it through NixlAgentMetadata.ple_block_len (optional, backward compatible) so the existing P/D geometry check still compares PLE pages. Region discovery is unchanged: the PLE keeps sharing its region with the pages it aliases, as test_csa_linear_registration_discovers_shared_regions expects; only the transfer length of its pages changes.

Test Plan

  • New unit test tests/v1/kv_connector/unit/test_nixl_ple_region.py: a CSA-linear-like config (MLA + GDN + ring + PLE sharing byte 0 of one block-outer tensor, PLE page larger than the MLA page); asserts the PLE descriptors cover the whole PLE page and that the page length is advertised in the handshake metadata. Before the fix the PLE descriptors carry the MLA page length.
  • tests/v1/kv_connector/unit/test_nixl_connector_hma.py + test_nixl_desc_geometry.py run inside the serving image (vLLM 109b5363fc) with the patch hot-patched: 307 passed before and after (one HF-download test deselected).
  • End-to-end on GB200 (vLLM 109b5363fc + this patch hot-patched into the image, Dynamo + NixlConnector, 1P/1D, nvidia/Qwen3.8-Flash-Next-NVFP4, MTP k=3, 200 requests of 64/64 tokens): mean acceptance length goes from 1.00 on 20/21 windows (zero accepted drafts) to 2.7–3.5 on every window, no NaN; the same configuration in aggregate mode gives ~2.1–2.4.

Test Result

See above; details and the full investigation (per-request probes of the PLE conv state on prefill and decode, per-column comparison of the transferred slot) are in #59642.

🤖 Generated with Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this should simply be nixl_agent_meta.ple_block_len so if the two workers are out of sync, we don't introduce a bug which can reset the behaviour that the PR is trying to solve.

@aijanai aijanai Oct 5, 2026 •

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.

Done in 63a74270ec: the remote side now reads nixl_agent_meta.ple_block_len directly and raises if the remote did not advertise it (different vLLM version), so an out-of-sync pair can never fall back to the truncated region length.

Comment thread vllm/distributed/kv_transfer/kv_connector/v1/nixl/base_worker.py Outdated
@aijanai
aijanai force-pushed the amassidda/fix-ple-conv-state-pd branch from 96871a7 to 63a7427 Compare October 5, 2026 17:02
@aijanai
aijanai marked this pull request as ready for review October 5, 2026 17:24

@claude claude 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Comment thread vllm/distributed/kv_transfer/kv_connector/v1/nixl/metadata.py Outdated
Comment thread vllm/distributed/kv_transfer/kv_connector/v1/nixl/base_worker.py Outdated
Comment thread tests/v1/kv_connector/unit/test_nixl_ple_region.py Outdated
@hmellor

hmellor commented Oct 6, 2026

Copy link
Copy Markdown
Member

@claude review

@hmellor hmellor added the verified Run pre-commit for new contributors without triggering other tests label Oct 6, 2026
@aijanai
aijanai force-pushed the amassidda/fix-ple-conv-state-pd branch from 63a7427 to eb13815 Compare October 6, 2026 14:18

@claude claude 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 found no issues

No high-confidence issues detected in this change.

This review covers commit 63a74270ec01fe85dd9a13c841f4e5f212048f8a, which is no longer the latest commit on this pull request; later commits are not covered by it.

@vadiklyutiy

Copy link
Copy Markdown
Member

@claude review

@vadiklyutiy

Copy link
Copy Markdown
Member

/ci run

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #93103 for commit 2528cf240ed8.

@vadiklyutiy
vadiklyutiy enabled auto-merge (squash) October 6, 2026 14:46
@github-actions github-actions Bot added the ready ONLY add when PR is ready to merge/full CI is needed label Oct 6, 2026

@claude claude 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 found no issues

No high-confidence issues detected in this change.

@vllm-agent

Copy link
Copy Markdown
Collaborator

CI selector (shadow): 14 test steps (18 jobs) instead of 19 (25 jobs)

Shadow mode: this changes nothing about what CI runs. It shows what the evidence-based selector would pick for this PR, next to today's rules. How it works.

Feedback welcome: reply here if it would skip a step this change needs, or runs something unrelated.

steps (jobs) Today's rules Selector Would skip Would add
NVIDIA, CPU and others 19 (25) 14 (18) 6 (8) 1 (1)
AMD mirrors 28 (31) 13 (16) 16 (16) 1 (1)
Selector would run (14)
  • crosslayer-kv-layout-distributed-nixlconnector-pd-accuracy-tests-4-gpus
  • distributed-flashinfer-nixlconnector-pd-accuracy-4-gpus
  • distributed-nixlconnector-pd-accuracy-4-gpus
  • dp-ep-distributed-nixlconnector-pd-accuracy-tests-4-gpus
  • engine
  • hybrid-ssm-nixlconnector-pd-accuracy-tests-4-gpus
  • hybrid-ssm-nixlconnector-pd-prefix-cache-2-gpus
  • kimi-k3-prefix-cache-4xb200 ×2
  • multiconnector-nixl-offloading-pd-accuracy-2-gpus
  • multiconnector-nixl-offloading-pd-edge-cases-2-gpus
  • nixlconnector-pd-edge-cases-2-gpus
  • push-nixlconnector-pp-prefill-pd-accuracy-4-gpus
  • v1-kv-connectors ×4
  • v1-others-cpu
Would skip (today's rules run them) (6)
  • ascend-npu-test
  • cpu-language-generation-and-pooling-model-tests ×3
  • v1-core
  • v1-executor-worker
  • v1-kv-offload
  • v1-metrics-lmeval
Would add (today's rules do not run them) (1)
  • engine (code map)
AMD mirrors: would skip (16)
  • extract-hidden-states-integration-2-gpus
  • fault-tolerance-e2e-2xh100
  • fusion-and-compile-unit-tests-2xb200
  • fusion-e2e-tp2-ar-rms-config-sweep-h100
  • fusion-e2e-tp2-asynctp-config-sweep-h100
  • fusion-e2e-tp2-b200
  • fusion-e2e-tp2-quick-h100
  • gemm-rs-ar-2xb200
  • kernels-fusedmoe-layer-test-2-b200s
  • kernels-fusedmoe-layer-test-2-h100s
  • kernels-minimax-reduce-rms-test-2-gpus
  • platform-tests
  • v1-core
  • v1-executor-worker
  • v1-kv-offload
  • v1-metrics-lmeval
AMD mirrors: would add (1)
  • engine (code map)

3 changed files · base 8bd7737a03 · head 2528cf240e · Python record: build 93039 at 1e5d0ea888 · kernel record: table 1e5d0ea (build 93039), map 1e5d0ea · not counted: 10 build steps, 5 A100 steps the generator no longer emits, 11 optional steps the selector would also run

@vadiklyutiy

Copy link
Copy Markdown
Member

/ci retry

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

✅ Queued 1 failed job(s) for retry in Buildkite CI #93103.

@vadiklyutiy

Copy link
Copy Markdown
Member

/ci cancel

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

✅ No cancelable CI build is running for branch amassidda/fix-ple-conv-state-pd.

@vadiklyutiy

Copy link
Copy Markdown
Member

/ci retry

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

✅ Queued 2 failed job(s) for retry in Buildkite CI #93103.

@aijanai

aijanai commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

CI failures seem due to infra instability. All the others are green.

arpera
arpera previously requested changes Oct 7, 2026
Comment thread vllm/distributed/kv_transfer/kv_connector/v1/nixl/metadata.py
auto-merge was automatically disabled October 7, 2026 10:54

Head branch was pushed to by a user without write access

@aijanai
aijanai force-pushed the amassidda/fix-ple-conv-state-pd branch from 2528cf2 to 97db14b Compare October 7, 2026 10:54
@aijanai
aijanai requested a review from arpera October 7, 2026 10:56
@aijanai

aijanai commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

@arpera it should be fine now

@arpera

arpera commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

@NickLucche, please, have a look this PR if you have some bandwidth

@vadiklyutiy

Copy link
Copy Markdown
Member

/ci run

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

❌ This PR is 99 commits behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@vadiklyutiy

Copy link
Copy Markdown
Member

/ci run

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #93641 for commit 891705ee328b.

Alberto Massidda and others added 4 commits October 8, 2026 18:26
The NIXL worker folds every layer that reports the same base address into one
region. In the block-outer HMA layout every cache group places its first page
at byte 0 of the shared allocation, so on Qwen3.8-Flash-Next the PLE short-conv
state (tp-replicated MambaSpec, page = channels x (conv_state_len +
num_spec_tokens)) is discovered in the region of layer-0 GDN, the QSA ring and
the MLA indexer cache, whose block_len is the MLA page. The PLE descriptors were
built from that block_len and transferred only 204800 of the 245760 bytes of
each PLE slot; the untransferred channels kept whatever the block held before
(blocks written by an async load are exempt from zeroing). With
num_speculative_tokens >= 2 the spec-decode short conv read those stale columns,
the layer-1 hidden state went NaN, and the rejection sampler rejected every
draft: 0% MTP acceptance in disaggregated P/D serving.

Record the PLE page length at registration and use it for the PLE descriptors
on both sides of the transfer, exchanging it through the handshake metadata so
the P/D geometry check still compares PLE pages.

Fixes vllm-project#59642

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Alberto Massidda <amassidda@nvidia.com>
…ck_len

The PLE page length is always recorded when the PLE region is registered,
and a remote that does not advertise it runs a different vLLM version:
raise instead of silently transferring the region's (smaller) block_len.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Alberto Massidda <amassidda@nvidia.com>
…xture

Advertising ple_block_len changes NixlAgentMetadata, so promote the
connector version and assert the field on the remote side. The regression
test now widens the PLE page of _make_csa_linear_ple_worker instead of
building a second fixture.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Alberto Massidda <amassidda@nvidia.com>
Signed-off-by: Alberto Massidda <amassidda@nvidia.com>
@aijanai
aijanai force-pushed the amassidda/fix-ple-conv-state-pd branch from 891705e to f0bc717 Compare October 8, 2026 16:26
@arpera

arpera commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

/ci run

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

❌ This PR is 2 commits behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@arpera

arpera commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

/ci run

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

@vadiklyutiy
vadiklyutiy dismissed arpera’s stale review October 8, 2026 19:57

Artem agreed that his comments fixed

@vadiklyutiy
vadiklyutiy merged commit 72d59ad into vllm-project:main Oct 8, 2026
72 checks passed
G-haoyu added a commit to G-haoyu/vllm that referenced this pull request Oct 9, 2026
Reconcile with vllm-project#59997 (PLE short-conv page transfer): a PLE page wider
than the main KV page it aliases now registers as its own region under
the (base_addr, block_len) key, so _ple_region_index points at a
dedicated region whose block_len is the full PLE page. Update
test_csa_linear_ple_page_wider_than_the_shared_region_page to the split
expectations; its descriptor assertions are unchanged since the PLE
descs already cover the whole page.

Co-authored-by: ZCode <noreply@zcode.ai>
andakai added a commit to aoshen02/vllm that referenced this pull request Oct 14, 2026
vllm-project#59625 landed on main squashed and reworked, so its files take main's
version and only the NIXL commit is replayed on top. Adaptations:
- The connector hook is unregister_kv_caches on main; NixlPullConnector
  now implements that name (the old release_kv_caches was never called).
- main took NIXL_CONNECTOR_VERSION 14 (vllm-project#59997); the registration epoch
  is version 15.
- A request whose peer re-registered handshakes directly from
  _start_read; other requests go through _read_blocks_for_req (vllm-project#55471).
- test_config follows main's refusal-only parametrization.

Signed-off-by: Dakai An <dakaian108@gmail.com>
michael-gpu pushed a commit to practical-inference/vllm-ampere-extended that referenced this pull request Oct 20, 2026
…n disaggregated P/D (vllm-project#59997)

Signed-off-by: Alberto Massidda <amassidda@nvidia.com>
Co-authored-by: Alberto Massidda <amassidda@nvidia.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Artem Perevedentsev <aperevedents@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working kv-connector ready ONLY add when PR is ready to merge/full CI is needed verified Run pre-commit for new contributors without triggering other tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Qwen3.8-flash-next 0% MTP acceptance rate in disaggregated PD serving

6 participants