Skip to content

[TE] Add rdma_twosided control-plane notify channel - #3440

Open
zhtshr wants to merge 3 commits into
kvcache-ai:mainfrom
zhtshr:feature/te-rdma-twosided-pr2
Open

[TE] Add rdma_twosided control-plane notify channel#3440
zhtshr wants to merge 3 commits into
kvcache-ai:mainfrom
zhtshr:feature/te-rdma-twosided-pr2

Conversation

@zhtshr

@zhtshr zhtshr commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Description

Second slice of the classic TE two-sided work (RFC #3377). Follows merged PR1 (#3324).

Adds an opt-in rdma_twosided transport whose control plane is a per-peer CtrlChannel (RC QP SEND/RECV). Classic rdma stays one-sided; a process installs either rdma or rdma_twosided.

This PR:

  • Adds RdmaTwoSidedTransport (MC_USE_RDMA_TWOSIDED=1) and wires MultiTransport mutual exclusion
  • Implements CtrlChannel + handshake fields (notify_qp_num / notify_rq_depth / ctrl_channel)
  • Routes sendNotify* over RDMA NOTIFY_COMPAT frames, with OOB fallback (MC_RDMA_NOTIFY_OOB_FALLBACK)
  • Adds rdma_notify_test (correctness + latency smoke; self-skips without an RDMA device)

No MsgChannel / bounce / managed-buffer data path yet (PR3). TE submitTransfer two-sided path remains PR4.

Module

  • Transfer Engine (mooncake-transfer-engine)
  • Mooncake Store (mooncake-store)
  • Mooncake EP (mooncake-ep)
  • Mooncake PG (mooncake-pg)
  • Integration (mooncake-integration)
  • P2P Store (mooncake-p2p-store)
  • Python Wheel (mooncake-wheel)
  • Common (mooncake-common)
  • Mooncake RL (mooncake-rl)
  • CI/CD
  • Docs
  • Other

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Documentation update
  • Performance improvement
  • Other

How Has This Been Tested?

Test commands:

ninja ctrl_frame_test sender_credit_test rdma_notify_test
./mooncake-transfer-engine/tests/ctrl_frame_test
./mooncake-transfer-engine/tests/sender_credit_test
MC_TE_FILTERS=erdma_0 MC_TEST_DEVICE_NAME=erdma_0 MC_MTU=1024 \
  ./mooncake-transfer-engine/tests/rdma_notify_test

Test results:

  • Unit tests pass (ctrl_frame_test / sender_credit_test)
  • rdma_notify_test 4/4 on erdma_0 (single / bidi / latency smoke ~10k ops/s / burst 128)
  • Integration tests pass (if applicable)
  • Manual testing done (describe below)

Notify latency smoke on eRDMA: 2000 notifies in ~191 ms, ~10.5k ops/s, avg ~95 µs.

Checklist

  • I have performed a self-review of my own code
  • I have formatted my code using ./scripts/code_format.sh
  • I have run pre-commit on touched files and hooks pass
  • I have updated the documentation (if applicable)
  • I have added tests to prove my changes are effective
  • For changes >500 LOC: I have filed an RFC issue

RFC: #3377

AI Assistance Disclosure

  • No AI tools were used
  • AI tools were used (specify below)

Cursor agent assisted implementing the rdma_twosided control-plane channel. Human submitter reviewed the PR2 scope (CtrlChannel + notify path only).

Made with Cursor

Install RdmaTwoSidedTransport as an opt-in alternative to classic rdma
and route sendNotify over a per-peer CtrlChannel, with OOB fallback.

Co-authored-by: Cursor <[email protected]>

Copilot AI 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.

Pull request overview

This PR adds an opt-in rdma_twosided Transfer Engine transport that introduces a per-peer RDMA RC SEND/RECV control-plane CtrlChannel to carry notify messages as typed control frames, while keeping the existing classic rdma one-sided data path unchanged and mutually exclusive with the new transport.

Changes:

  • Introduces RdmaTwoSidedTransport and CtrlChannel, wiring handshake fields (notify_qp_num, notify_rq_depth, ctrl_channel) and a background poll worker for CtrlChannel completions.
  • Routes sendNotify* through the CtrlChannel RDMA notify path with configurable OOB fallback behavior.
  • Adds an RDMA notify correctness/latency smoke test (rdma_notify_test) and integrates it into the test build.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
mooncake-transfer-engine/tests/rdma_notify_test.cpp Adds RDMA notify correctness + latency smoke tests (self-skips without an RDMA device).
mooncake-transfer-engine/tests/CMakeLists.txt Builds/registers rdma_notify_test and labels it as RDMA.
mooncake-transfer-engine/src/transport/rdma_twosided/rdma_twosided_transport.cpp Implements the new transport wrapper and hooks CtrlChannel setup into connection establishment.
mooncake-transfer-engine/src/transport/rdma_twosided/ctrl_plane.cpp Implements ctrl-channel setup, notify routing, and the ctrl worker polling loop.
mooncake-transfer-engine/src/transport/rdma_twosided/ctrl_channel.cpp Implements the per-peer RC QP CtrlChannel (resource lifecycle, handshake connect, SEND/RECV, CQ polling).
mooncake-transfer-engine/src/transfer_metadata.cpp Extends handshake JSON encode/decode for CtrlChannel fields; adds pushNotify() to inject notifies from non-OOB paths.
mooncake-transfer-engine/src/transfer_engine_impl.cpp Routes notify sending through RDMA CtrlChannel when available, with optional OOB fallback.
mooncake-transfer-engine/src/multi_transport.cpp Enforces mutual exclusion between rdma and rdma_twosided; routes “rdma” segments to twosided transport when installed.
mooncake-transfer-engine/src/config.cpp Adds env-driven configuration for installing rdma_twosided and tuning RDMA notify behavior.
mooncake-transfer-engine/include/transport/rdma_twosided/rdma_twosided_transport.h Declares RdmaTwoSidedTransport public API and ctrl-worker/channel management.
mooncake-transfer-engine/include/transport/rdma_twosided/ctrl_channel.h Declares the per-peer CtrlChannel interface (connect, send frame/notify, poll, disconnect).
mooncake-transfer-engine/include/transport/rdma_transport/rdma_transport.h Makes onSetupRdmaConnections virtual to allow transport override.
mooncake-transfer-engine/include/transfer_metadata.h Adds CtrlChannel handshake fields and pushNotify() API.
mooncake-transfer-engine/include/transfer_engine_impl.h Adds config dependency for choosing rdma_twosided as default protocol when enabled.
mooncake-transfer-engine/include/config.h Adds new global config toggles/knobs for rdma_twosided + RDMA notify path.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +168 to +174
int processed = 0;
for (auto &channel : channels) {
processed += channel->pollCompletions(16);
}
if (processed == 0) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
Comment on lines +139 to +143
if (root.isMember("notify_rq_depth") &&
root["notify_rq_depth"].isUInt()) {
desc.notify_rq_depth =
static_cast<uint16_t>(root["notify_rq_depth"].asUInt());
}
Reject out-of-range notify_rq_depth, avoid spinning on poll errors, and
self-skip rdma_notify_test when CI enumerates an unusable mlx5_0.

Co-authored-by: Cursor <[email protected]>
return nullptr;
}
lock.lock();
auto again = ctrl_channels_.find(peer_server_name);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does multiple callers can still create channels concurrently?

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.

Yes. The old re-check after handshake did not serialize in-flight connects.

We now insert a placeholder into ctrl_channels_ before the handshake. Other callers wait on ctrl_cv_ until the entry is connected, replaced, or erased, instead of starting another connectActive().

Comment on lines +575 to +578
void CtrlChannel::disconnect() {
std::lock_guard<std::mutex> lock(resource_mutex_);
destroyResources();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This introduces a lock-order inversion: sendCtrlFrame() holds send_mutex_ before acquiring resource_mutex_, while disconnect() holds resource_mutex_ and then calls destroyResources(), which acquires send_mutex_. Concurrent send and channel replacement can therefore deadlock. Please use a consistent lock order or split shutdown into non-nested state-transition and resource-destruction phases.

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.

Agreed. sendCtrlFrame() took send_mutex_ then resource_mutex_, while disconnect() nested them the other way.

disconnect() no longer nests the locks: it wakes send waiters, drops send_mutex_, then tears down IB resources under resource_mutex_ only.

Placeholder entries stop concurrent active handshakes to the same peer,
and disconnect no longer nests send/resource mutexes in reverse order.

Co-authored-by: Cursor <[email protected]>
Comment on lines +87 to +95
std::unique_lock<std::mutex> lock(ctrl_mutex_);
while (true) {
auto it = ctrl_channels_.find(peer_server_name);
if (it != ctrl_channels_.end() && it->second) {
if (it->second->connected()) return it->second;
// Active or passive connect is already in flight for this peer.
ctrl_cv_.wait(lock);
continue;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How to avoid ctrl_cv_ wait indefinitely?

return 0;
}

int CtrlChannel::sendNotify(const NotifyDesc &notify) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should be called after receiving WCs instead of submitting WRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants