Skip to content

[Bugfix] PLE offload: send registration inputs by file descriptor - #654

Open
Peuqui wants to merge 1 commit into
1CatAI:mainfrom
Peuqui:ple-registration-fd-sharing
Open

Peuqui wants to merge 1 commit into
1CatAI:mainfrom
Peuqui:ple-registration-fd-sharing

Conversation

@Peuqui

@Peuqui Peuqui commented Sep 18, 2026

Copy link
Copy Markdown

Purpose

Unloading a model that runs the PLE offload worker (VLLM_PLE_CPU_OFFLOAD)
regularly ends in an abort instead of a clean exit:

terminate called after throwing an instance of 'std::system_error'
  what():  Broken pipe

The process dies with SIGABRT and leaves a core dump; on Ubuntu, apport turns
each one into a crash report. It happens in the offload worker or in a GPU
worker, depending on the teardown order.

Cause: PleOffloadConnector._register_with_offload_worker pickles the
registration with the file_system sharing strategy. The CPU input buffers were
created with share_memory_() under the default file_descriptor strategy, so
pickling them under file_system moves their storage into a new segment owned
by a torch_shm_manager (the existing comment "ForkingPickler may replace CPU
storage while converting its sharing strategy" refers to this). From then on the
GPU worker and the offload process both release that storage through the
manager. On teardown the manager can exit first; libshm then throws
std::system_error (EPIPE) inside the storage destructor, which calls
std::terminate.

Fix: serialize the registration with file_descriptor (new helper
_dump_registration). The buffers keep their storage, no manager is started,
and every process releases its own mapping. CUDA tensors in the registration
still travel through CUDA IPC; nothing else changes.

Why this is not a duplicate: gh pr list --repo 1CatAI/1Cat-vLLM --state open
with the searches "PLE offload", "PleOffload", "Broken pipe", "sharing
strategy", "file_system", "torch_shm_manager" and "SIGABRT" returns only my
own #646, which does not touch the sharing strategy (it adds
remote_placements to the same registration, a separate hunk). No issue
reports this crash; #530 is about PLE offload GPU memory, not teardown.

Test Plan

On origin/main (b711d53) plus this change:

.venv/bin/python -m pytest tests/v1/worker/test_ple_offload_worker.py -v
pre-commit run --files vllm/v1/ple_offload/connector.py tests/v1/worker/test_ple_offload_worker.py
pre-commit run mypy-3.10 --hook-stage manual --files vllm/v1/ple_offload/connector.py tests/v1/worker/test_ple_offload_worker.py

Two new CPU-only tests:

  • test_ple_registration_keeps_input_storage_without_shm_manager: after
    _dump_registration the input buffer still has the same data_ptr, no
    torch_shm_manager child exists, and the global sharing strategy is restored.
  • test_ple_registration_outlives_its_sender: a spawned sender registers shared
    input buffers, the receiver reads a value the sender writes afterwards, the
    sender is killed with SIGKILL, and the receiver releases the buffers and exits
    with code 0. The child imports the vllm under test via PYTHONPATH, since a
    script's sys.path starts at its own directory.

Counter-check: with file_system restored in _dump_registration, both new
tests fail (the data_ptr changes; managers=1).

End to end: nvidia/Qwen3.8-Flash-Next-NVFP4 with MTP, TP2 x PP2 on
2x Quadro RTX 8000 + 2x V100-PCIE-32GB, PLE offload worker enabled, loaded and
unloaded by llama-swap (one request per load, then unload). This ran on my fork,
which carries this exact change on top of other open work (#646 and its
dependencies); the touched function is identical to main.

Test Result

  • tests/v1/worker/test_ple_offload_worker.py: 29 passed (27 existing, 2 new).
  • Counter-check with file_system: the 2 new tests fail as expected.
  • pre-commit: all hooks passed; mypy-3.10 --hook-stage manual: passed.
  • End to end, before: 2 of 4 unloads aborted in production that day (the
    offload worker both times). With an interim workaround that only skipped the
    destructors in the offload worker, the GPU worker Worker_PP0_TP0 still
    aborted in 2 of 3 unloads, so both sides are affected.
  • End to end, after: 3 of 3 unloads exited cleanly, no terminate called in the
    log, no crash report. The three torch_* segments per load that the dead
    manager used to leave in /dev/shm are gone. (Two other small torch_*
    segments per load from the GPU workers remain; they predate this change and
    are unrelated to PLE.)

AI assistance (Claude) was used to find the cause, write the fix and tests, run
the measurements and write this description. I reviewed every changed line and
ran the tests and the end-to-end check on the hardware named above.


Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan, such as providing test command.
  • The test results, such as pasting the results comparison before and after, or e2e results
  • (Optional) The necessary documentation update, such as updating supported_models.md and examples for a new model.

🤖 Generated with Claude Code

The GPU worker pickled its offload registration with the "file_system"
sharing strategy. Its CPU input buffers were created with share_memory_()
under the default "file_descriptor" strategy, so pickling them under
"file_system" moved their storage into a new segment owned by a
torch_shm_manager, and from then on the GPU worker and the offload process
both released that storage through the manager. When the engine is torn
down, the manager can exit first. libshm then throws std::system_error
(EPIPE) inside the storage destructor and the process dies with
"terminate called after throwing an instance of 'std::system_error'
what(): Broken pipe" (SIGABRT, core dump) instead of exiting. Depending on
the order, the offload worker or the GPU worker aborted.

Serialize the registration with "file_descriptor" instead. The buffers keep
their storage, no manager is started, and each process releases its mapping
locally.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Peuqui <peuqui@github.com>
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