Skip to content

[None][fix] Skip empty aux buffers when registering NIXL memory - #16948

Closed
qiaoxj07 wants to merge 2 commits into
NVIDIA:mainfrom
qiaoxj07:xqiao/fix-nixl-libfabric-null-aux-desc
Closed

[None][fix] Skip empty aux buffers when registering NIXL memory#16948
qiaoxj07 wants to merge 2 commits into
NVIDIA:mainfrom
qiaoxj07:xqiao/fix-nixl-libfabric-null-aux-desc

Conversation

@qiaoxj07

@qiaoxj07 qiaoxj07 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Description

The draft-token aux buffer is allocated as torch.empty(max_slot_num, max_draft_len), so when
max_draft_len == 0 (the default with speculative decoding off) it has numel() == 0 and
data_ptr() == 0.

TransferWorker._register_aux_buffer registered all four aux pointers unconditionally, handing that
null address to NIXL. The LIBFABRIC backend rejects it outright — libfabric_rail_manager.cpp
if (!buffer) { NIXL_ERROR << "Invalid buffer parameter"; } — which fails the entire registration
and aborts transceiver setup:

E libfabric_rail_manager.cpp:759] Invalid buffer parameter
E libfabric_backend.cpp:932]      Rail Manager registerMemory failed
E nixl_agent.cpp:545]             registerMem: registration failed
RuntimeError: Assertion failed: status == NIXL_SUCCESS (transferAgent.cpp:702)
  NixlTransferAgent::registerMemory(MemoryDescs const&)

That string occurs at exactly one site in NIXL and its only precondition is buffer != nullptr, so the
failure is purely the null descriptor — no alignment, size, or memory-type constraint is involved.

Two reasons this went unnoticed:

  • UCX tolerates the null descriptor, so it only reproduces with TRTLLM_NIXL_KVCACHE_BACKEND=LIBFABRIC.
  • The C++ transceiver is unaffected — its only registerMemory call site
    (agent_utils/connection.cpp) registers just the CacheTransBufferManager staging buffers, never the
    aux buffer. Only the Python transceiver registers aux memory.

This blocks cache_transceiver_config.transceiver_runtime: PYTHON + LIBFABRIC entirely, which in turn
blocks EFA for any model pinned to the V2 KV-cache manager.

Fix

Skip zero-pointer / zero-size buffers, and skip the registration call entirely if nothing is left.
Empty buffers carry no data, so nothing is lost. Descriptor names stay tied to the original buffer
index so peer matching is unchanged.

Test Coverage

New tests/unittest/disaggregated/test_aux_buffer_registration.py (4 cases, no GPU required): null
pointer skipped, zero size skipped, all-non-empty unchanged (including desc names), and nothing
registered when every buffer is empty.

Validated end-to-end on 2 nodes x 2 GPU over AWS EFA (16 rails), NIXL + LIBFABRIC:

combination before after
NIXL / PYTHON / V1 TRANSFER_ERROR PASS, 96.7 GB/s per GPU

For reference the C++ transceiver reaches 93.6 GB/s per GPU on the same setup; the Python path is
slightly faster since it is zero-copy and skips the staging-buffer hop.

PR Checklist

  • PR title follows [JIRA/NVBUG/None][type] Summary
  • Commit is signed off (DCO)
  • New tests added
  • Change is limited to one concern

Dev Engineer Review

  • Updated TransferWorker._register_aux_buffer to skip auxiliary-buffer slots where the aux pointer (data_ptr()) is 0 or the aux size is 0, preventing NIXL/LIBFABRIC registration failures when speculative decoding leaves auxiliary buffers empty.
  • Added an early return when all aux slots are empty, so _agent.register_memory is not called with no valid (ptr, size) descriptors.
  • Preserved descriptor-to-buffer index mapping by keeping the original per-slot index in the descriptor name (aux_buffer_ptr_{i}) for surviving entries.

QA Engineer Review

Touched:

  • tests/unittest/disaggregated/test_aux_buffer_registration.py
  • tests/integration/test_lists/test-db/l0_a10.yml
  • tests/integration/test_lists/test-db/l0_h100.yml

Test functions added:

  • test_null_pointer_aux_buffer_is_skipped()
  • test_zero_size_aux_buffer_is_skipped()
  • test_all_buffers_registered_when_none_are_empty()
  • test_nothing_registered_when_every_buffer_is_empty()

Integration coverage (test-db/):

  • Added unittest/disaggregated/test_aux_buffer_registration.py to:
    • tests/integration/test_lists/test-db/l0_a10.yml
    • tests/integration/test_lists/test-db/l0_h100.yml

Verdict: needs follow-up

@qiaoxj07
qiaoxj07 requested a review from a team as a code owner July 28, 2026 10:42
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fbf2563c-b320-483a-892b-384ef1a18989

📥 Commits

Reviewing files that changed from the base of the PR and between 7158463 and bab9f07.

📒 Files selected for processing (3)
  • tests/integration/test_lists/test-db/l0_a10.yml
  • tests/integration/test_lists/test-db/l0_h100.yml
  • tests/unittest/disaggregated/test_aux_buffer_registration.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unittest/disaggregated/test_aux_buffer_registration.py

Walkthrough

Changes

Auxiliary-buffer registration now skips null-pointer or zero-sized descriptors and avoids registration when all entries are empty. New unit tests cover filtering, complete registration, descriptor index preservation, the all-empty case, and pre-merge test-list inclusion.

Auxiliary buffer registration

Layer / File(s) Summary
Filter empty auxiliary buffers
tensorrt_llm/_torch/disaggregation/native/transfer.py
_register_aux_buffer excludes entries with pointer 0 or size 0, and returns before registration when no valid descriptors remain.
Validate registration behavior
tests/unittest/disaggregated/test_aux_buffer_registration.py, tests/integration/test_lists/test-db/l0_a10.yml, tests/integration/test_lists/test-db/l0_h100.yml
Test helpers and four cases verify skipped entries, full registration, descriptor index mapping, and no registration for entirely empty buffers; the test is added to A10 and H100 pre-merge lists.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: qijune

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately summarizes the main fix to skip empty aux buffers during NIXL memory registration.
Description check ✅ Passed The description includes clear problem, fix, test coverage, and checklist sections with enough detail to meet the template.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/disaggregated/test_aux_buffer_registration.py`:
- Around line 47-56: The test test_null_pointer_aux_buffer_is_skipped should
isolate null-pointer filtering from zero-size filtering. Give the skipped buffer
a non-zero size, update the expected registered descriptors accordingly, and
assert their names are the sparse sequence aux_buffer_ptr_0, aux_buffer_ptr_2,
and aux_buffer_ptr_3 alongside the existing pointer and registration assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 05061793-ad1c-407e-816b-b1b2408d65c9

📥 Commits

Reviewing files that changed from the base of the PR and between 90f7868 and c80fde4.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/disaggregation/native/transfer.py
  • tests/unittest/disaggregated/test_aux_buffer_registration.py

Comment thread tests/unittest/disaggregated/test_aux_buffer_registration.py Outdated
@qiaoxj07

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62181 [ run ] triggered by Bot. Commit: f73628b Link to invocation

The draft-token aux buffer is allocated as torch.empty(max_slot_num,
max_draft_len), so with max_draft_len == 0 (the default when speculative
decoding is off) it has numel() == 0 and data_ptr() == 0.

_register_aux_buffer registered all four aux pointers unconditionally,
handing that null address to NIXL. The LIBFABRIC backend rejects it
outright -- libfabric_rail_manager.cpp `if (!buffer)` -> "Invalid buffer
parameter" -- which fails the entire registration and aborts transceiver
setup:

  Assertion failed: status == NIXL_SUCCESS (transferAgent.cpp)
    NixlTransferAgent::registerMemory(MemoryDescs const&)

UCX tolerates the null descriptor, so this only reproduced with
TRTLLM_NIXL_KVCACHE_BACKEND=LIBFABRIC. The C++ transceiver is unaffected
because it registers only the CacheTransBufferManager staging buffers.

Skip zero-pointer/zero-size buffers, and skip the registration entirely
when nothing is left. Empty buffers carry no data, so nothing is lost.

Verified on 2 nodes x 2 GPU over AWS EFA (16 rails): the
NIXL/PYTHON/LIBFABRIC case goes from TRANSFER_ERROR to PASS at
96.7 GB/s per GPU.

Signed-off-by: Xianjie <5410381+qiaoxj07@users.noreply.github.com>
@qiaoxj07
qiaoxj07 force-pushed the xqiao/fix-nixl-libfabric-null-aux-desc branch from f73628b to 7158463 Compare July 28, 2026 11:13
@qiaoxj07

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/disaggregated/test_aux_buffer_registration.py`:
- Around line 33-43: Add complete parameter and return type annotations to
_fake_worker, _registered_descs, and each of the four test functions in this
diff. Use precise types for helper inputs and outputs, and annotate every test
function with -> None without changing their behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f780af62-52de-4bd9-95fe-ee12bceaf409

📥 Commits

Reviewing files that changed from the base of the PR and between f73628b and 7158463.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/disaggregation/native/transfer.py
  • tests/unittest/disaggregated/test_aux_buffer_registration.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_torch/disaggregation/native/transfer.py

Comment thread tests/unittest/disaggregated/test_aux_buffer_registration.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62184 [ run ] triggered by Bot. Commit: 7158463 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62181 [ run ] completed with state ABORTED. Commit: f73628b

Link to invocation

@qiaoxj07
qiaoxj07 requested a review from chuangz0 July 28, 2026 11:30
Register the new test file in the l0_h100 and l0_a10 test lists -- without
an entry it is never executed by CI.

Give the null-pointer case a non-zero size so it fails if only zero-sized
buffers are filtered, and assert the surviving descriptors keep their
original indices (aux_buffer_ptr_0/2/3) rather than being renumbered.

Annotate the new functions, matching the convention in the neighbouring
disaggregated unit tests.

Baseline-fails check on 1.3.0rc22: 4/4 pass with the fix, 1/4 without it
(only the no-empty-buffers control passes).

Signed-off-by: Xianjie <5410381+qiaoxj07@users.noreply.github.com>
@qiaoxj07

Copy link
Copy Markdown
Collaborator Author

Thanks @coderabbitai — all three points were valid and are addressed in bab9f079.

1. Test list registration (the important one). The file had no entry under tests/integration/test_lists/, so the tests would never have run in CI. Every neighbouring file in tests/unittest/disaggregated/ is registered, so this was simply an omission on my side. Added to l0_h100.yml and l0_a10.yml.

2. Null-pointer vs zero-size independence. Correct — the skipped entry previously had both ptr == 0 and size == 0, so the test would still have passed against a size-only filter. It now uses size=256 with ptr=0, and asserts the survivors keep their original indices (aux_buffer_ptr_0/2/3) rather than being renumbered, which is what keeps peer matching correct.

3. Type annotations. Added. For the record, CODING_GUIDELINES.md does not contain the quoted "Annotate every function" wording, but the convention is clearly established locally — test_transceiver_bounded_polling.py annotates 14/14 test functions — so the suggestion stands on that basis.

Validated with a baseline-fails check on 1.3.0rc22 (real container, not mocks):

image result
with the fix 4/4 pass
without the fix 1/4 pass — the three fix-specific tests fail; only the no-empty-buffers control passes

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@qiaoxj07 Thanks for the thorough follow-up. The CI test-list registration, independent null-pointer coverage with original-index assertions, and local type-annotation consistency all address the findings well. The baseline-fails validation is especially strong evidence that the new tests exercise the intended regression.

🐇 ✅

@qiaoxj07

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62187 [ run ] triggered by Bot. Commit: bab9f07 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62184 [ run ] completed with state ABORTED. Commit: 7158463

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62187 [ run ] completed with state SUCCESS. Commit: bab9f07
/LLM/main/L0_MergeRequest_PR pipeline #50358 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

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