You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
shengtiedan
committed
fix _read_buf race between PollCq and OnNewMessages in RDMA server
The server-side RDMA socket's _read_buf is accessed by two independent
bthreads: PollCq (CQ socket) writes RDMA data via HandleCompletion and
calls ProcessNewMessage (which reads _read_buf via CutInputMessage), and
OnNewMessages (main socket) reads TCP data for handshake / fallback.
Since IOBuf is not thread-safe, concurrent access corrupts internal
state and causes intermittent core dumps.
Three fixes:
1. Switch edge trigger to OnNewDataFromTcp in ALL ExecuteServerHandshake
end paths (ESTABLISHED + 5 failure paths). OnNewDataFromTcp checks the
RDMA state: in ESTABLISHED it only reads 1 byte for EOF detection
without touching _read_buf; in FALLBACK_TCP it delegates to
OnNewMessages for TCP data. This prevents post-handshake races.
2. Guard HandleCompletion (IBV_WC_RECV) with a state check: skip writing
to _read_buf if the state is not ESTABLISHED, but still handle imm
data, re-post the recv WR (with failure check), and send ack. This
prevents races during the handshake (after BringUpQp puts the QP into
RTS, the client may start sending RDMA data before the server
finishes processing the ACK).
3. Remove the source->size() > HELLO_ACK_LEN check in Phase 2. When a
client falls back to TCP, the 4-byte ACK and the first RPC request
may arrive in the same readv() call. Use cutn() to drain the 4-byte
ACK and let remaining data be processed by other parsers, matching
FallbackServerHandshake's behavior.
Additionally:
- Return NOT_ENOUGH_DATA (not TRY_OTHERS) from the ESTABLISHED path so
CutInputMessage returns immediately without reading _read_buf,
minimizing the race window with PollCq.
- Clear _read_buf before transitioning to ESTABLISHED so that residual
TCP data cannot become a prefix of the RDMA recv stream (HandleCompletion
appends to _read_buf, not overwrites). The clear is safe because
HandleCompletion only writes after seeing ESTABLISHED (acquire), which
is stored (release) strictly after the clear.
- Use memory_order_release for ESTABLISHED stores (both client and server)
to properly pair with the acquire load in HandleCompletion.
- Restore edge trigger in RdmaTransport::Reset() based on CreatedByConnect():
OnNewDataFromTcp for client-side sockets, OnNewMessages for server-side
sockets, matching the logic in Init().
- Add Transport::ShouldStopReading() virtual method (default false),
overridden by RdmaTransport to return true when the RDMA endpoint has
reached ESTABLISHED (via RdmaEndpoint::IsEstablished(), which uses an
acquire load on the already-atomic _state, pairing with the release
store in ExecuteServerHandshake). OnNewMessages checks this after
ProcessNewMessage returns and exits immediately, preventing it from
calling DoRead again on _read_buf after the edge trigger has been
switched.
- Guard ProcessNewMessage in PollCq with bytes > 0: when bytes == 0
(IBV_WC_SEND completions, or IBV_WC_RECV dropped during handshake),
skip ProcessNewMessage entirely. This prevents PollCq from calling
CutInputMessage on _read_buf (via ProcessNewMessage) while
OnNewMessages is driving the handshake on the same _read_buf.
0 commit comments