Skip to content

feat(kv-offload): export KV via VMM dma-buf fd instead of CUDA IPC - #740

Closed
xiaguan wants to merge 6 commits into
mainfrom
feat/external-pegaflow-vmm
Closed

feat(kv-offload): export KV via VMM dma-buf fd instead of CUDA IPC#740
xiaguan wants to merge 6 commits into
mainfrom
feat/external-pegaflow-vmm

Conversation

@xiaguan

@xiaguan xiaguan commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Switches the external-PegaFlow KV offload client from CUDA IPC handles to VMM allocations exported as POSIX file descriptors.

Why: a CUDA-IPC-imported pointer cannot be registered into the NIC (ibv_reg_mr on a cuIpcOpenMemHandle pointer fails, and no dma-buf fd can be derived), so it can never back GPUDirect RDMA. A VMM allocation (cuMemCreate + POSIX-fd export) can be handed to ibv_reg_dmabuf_mr. This PR swaps the sharing primitive so RDMA becomes possible; host-tier save/load behavior is unchanged.

A POSIX fd cannot travel inside a gRPC message, so the export fd is sent to PegaFlow over its host-local Unix-domain fd side-channel (SCM_RIGHTS) before registration; the gRPC request carries only per-layer strided metadata plus the aligned allocation size.

Changes

  • exportable.rs: VmmExportableBuffer allocates the fused KV arena via cuMemCreate (POSIX-fd handle), reserves/maps it, and exports an fd. gpuDirectRDMACapable is set only where the device attribute reports support — consumer parts reject it in cuMemCreate, data-center parts (H100/H200/B200) accept it.
  • buffer.rs: KvBuffer holds the VMM allocation plus a ManuallyDrop<CudaSlice> view for the attention kernels; the view never frees the VMM pointer (teardown goes through unmap/free/release, not cuMemFree).
  • external.rs: send the export fd over PegaFlow's fd side-channel (SCM_RIGHTS) before register; build per-layer NativeKvTensor metadata and pass the granularity-aligned native_alloc_size.
  • engine.rs: thread the export fd + alloc size from the buffer through registration. The multi-arena path (GLM5.2) is left on None — VMM export for it is future work; this PR covers the single fused Qwen3 buffer.

Dependency

Requires the matching PegaFlow native-VMM server: novitalabs/pegaflow#414. The dep is pinned to that commit (6e0baa7); repin to the merged rev before this lands.

Testing

  • Qwen3-4B host-tier save/load verified end to end: a prefix evicted from HBM is restored from the CPU tier bit-exact (kv_offload_cpu_hit::live_gpu_and_cpu_prefix_hits, head-logprob delta 0.0000).
  • Verified against both the default PegaFlow server and its torch-free (--python-registry false) mode.

🤖 Generated with Claude Code

xiaguan and others added 6 commits July 19, 2026 15:59
Signed-off-by: xiaguan <751080330@qq.com>
Signed-off-by: xiaguan <751080330@qq.com>
Signed-off-by: xiaguan <751080330@qq.com>
Signed-off-by: xiaguan <751080330@qq.com>
Signed-off-by: xiaguan <751080330@qq.com>
Switch the external-PegaFlow KV offload client from CUDA IPC handles to
VMM (cuMemCreate) allocations exported as POSIX file descriptors. A CUDA
IPC imported pointer cannot be registered into the NIC, so it can never
back GPUDirect RDMA; a VMM dma-buf fd can. host-tier save/load is
unchanged in behavior — this swaps the sharing primitive so RDMA becomes
possible later.

- exportable: VmmExportableBuffer allocates the fused KV arena via
  cuMemCreate (POSIX-fd handle), maps it, and exports an fd. The
  gpuDirectRDMACapable flag is set only where the device attribute
  reports support (consumer parts reject it in cuMemCreate).
- buffer: KvBuffer holds the VMM allocation and a ManuallyDrop CudaSlice
  view for the attention kernels; the view never frees the VMM pointer
  (teardown goes through unmap/free/release).
- external: send the export fd to PegaFlow over its Unix-domain fd
  side-channel (SCM_RIGHTS) before register; the gRPC request carries
  only per-layer strided metadata plus the aligned allocation size.

Requires the matching PegaFlow native-VMM server (pinned rev). Verified
end to end on Qwen3-4B: an evicted prefix restores from the CPU tier
bit-exact.

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e56f8a71d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +549 to +553
self.flush_saves();
if let Err(err) = self
.host
.runtime
.block_on(self.host.client.unregister(&self.instance_id))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Wait for pending saves before unregistering

When a save or server flush takes longer than the 5s FLUSH_DEADLINE, flush_saves_then reports completion but drops the JoinHandles rather than aborting the save tasks, so those RPCs can continue using the exported CUDA allocation. shutdown() then unregisters the context here and the subsequent drop can release the VMM buffer while the server may still be DMAing from it (the save RPC timeout is 30s), which defeats the new ownership-safety checks. The shutdown path should either wait for/abort on all outstanding saves instead of using the bounded flush, or not unregister/release the mapping until they are definitively complete.

Useful? React with 👍 / 👎.

Comment on lines +312 to +314
// See `with_arenas`: multi-arena VMM export is future work.
None,
None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pass exported fds for arena registrations

This multi-arena path is still reachable from GLM5.2 build_offload_engines() for every --kv-offload-server launch, but ExternalClient::register now treats a missing export_fd/alloc_size as a hard InvalidArgument because VMM registration has no fallback sharing mechanism. As a result, GLM5.2 KV offload always fails during rank registration despite the CLI/docs enabling it; either make these arenas VMM-exportable and pass the fd metadata, or reject GLM offload before startup until that migration is implemented.

Useful? React with 👍 / 👎.

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