Files
versitygw/cuwrapper/rc/v2-transport.h
T
Jihyeon Gim efa0309da4 rdma: port the hipObject v2 RC session core
Port the hipObject v2 reliable-connection session core into
cuwrapper/rc: the session table and state machine, the wire
codec for the hipobj-rc-v2 headers, request parsing, the
injectable clock and randomness sources, the transport layer
(QP/CQ lifecycle, RTR/RTS transitions, staging registration),
the data phase (RDMA write with immediate for GET, receive with
immediate for PUT), the RDMA token codec, and the dynamically
loaded ibverbs shim (ibv-core.h plus the dlopen host binding).

The sources are a port of the upstream hipObject v2 core, kept
close to the original so the two trees can be diffed during
review. Nothing links against them yet; a Makefile rule builds
the objects into rdma/librcserver.a for the ABI layer that
follows.

Signed-off-by: Jihyeon Gim <potatogim@potatogim.net>
2026-08-30 00:00:29 +09:00

107 lines
4.1 KiB
C++

/* Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
* Copyright (c) Gluesys Inc. and Jihyeon Gim. All rights reserved.
*
* SPDX-License-Identifier: MIT
*/
/* v2 transport: device/qp ownership split and release procedure.
*
* DeviceHandle is the shared half (context, protection domain,
* topology) with a connection reference count; RcConnV2 owns one
* qp/cq pair. v2 entry points route through these helpers so the
* registry, capacity accounting, and the retired ring stay
* consistent. Everything runs under v2::apiLock(). */
#pragma once
#include <atomic>
#include <cstdint>
#include "ibv-core.h"
#include "v2-registry.h"
namespace hipObj {
struct DeviceHandle {
struct ibv_context* ctx = nullptr;
struct ibv_pd* pd = nullptr;
uint8_t portNum = 1;
int gidIndex = -1;
union ibv_gid localGid = {};
std::atomic<uint32_t> connRefs{0};
};
namespace v2 {
/* Creates the qp/cq pair on dh. On success conn holds both objects,
* conn.qpNum is captured, and dh->connRefs was incremented. On
* failure every partially created object was rolled back (rollback
* failures are reported via *rollbackFailed so the caller can raise
* a poisoned tombstone). */
int createRcConnV2(DeviceHandle* dh, RcConnV2& conn,
bool* rollbackFailed = nullptr);
/* Destroys conn's qp and cq. Reflects per-object success through
* the out flags; the caller feeds them to commitDestroy(). */
void destroyRcConnV2(RcConnV2& conn, bool* qpOk, bool* cqOk);
/* QP state transitions taking the device topology from dh. The v1
* signatures (RcConnection&) remain unchanged. */
int transitionQpToInitV2(DeviceHandle* dh, RcConnV2& conn);
int transitionQpToRtrV2(DeviceHandle* dh, RcConnV2& conn, uint32_t destQpNum,
uint16_t destLid, union ibv_gid destGid,
uint32_t rqPsn);
int transitionQpToRtsV2(RcConnV2& conn, DeviceHandle* dh, uint32_t sqPsn);
/* Drives a QP from any state (including RTS/ERR after a failed
* transfer) back to INIT. The verbs state table has no direct
* RTS->INIT edge, so this goes through RESET, which is valid from
* every state. */
int rearmQpToInitV2(DeviceHandle* dh, RcConnV2& conn);
/* Creates only a qp on an existing cq (conflict-discard retry uses
* this so the original cq survives). */
int createRcQpOnly(DeviceHandle* dh, struct ibv_cq* cq, RcConnV2& conn);
/* Retires a live qp whose (qpn, psn) was already used by a peer:
* reserves a fresh ring slot, destroys the old qp, records the
* tuple, and recreates the qp on the existing cq. On any failure
* the entry keeps its live qp and reservation (callers surface the
* error); the busy return means the retired ring is exhausted. */
int discardAndRecreateQp(ConnId id);
/* Decrements dh->connRefs after a successful releaseRcConnV2 and
* closes ctx/pd when no MR and no connection remain. */
void releaseDevice(DeviceHandle* dh);
/* Full v2 release procedure for one registry entry: claim, reserve
* a retired slot for a live qp, destroy, commit, record, erase.
* Returns a hipObj error code (0 = success, busy = deferred,
* rdma = leftover/poison). */
int releaseConnection(ConnId id);
/* Posts the zero-SGE receive work request (wr_id = kRecvImm). */
constexpr uint64_t kRecvImm = 0x5245435632494d4dULL; /* "RECV2IMM" */
int postRecvImm(DeviceHandle* dh, RcConnV2& conn);
/* Completion validation for the data phase. A GET consumes one
* RECV_RDMA_WITH_IMM completion; a PUT consumes one RECV completion
* carrying the immediate (flags & IBV_WC_WITH_IMM). The immediate
* carries the session cookie in network order - the byte count
* travels in the FINAL response instead. */
enum class WcKind { kGet, kPut };
struct WcExpectation {
WcKind kind;
uint64_t wrId; /* expected wr_id (kRecvImm for receives) */
uint32_t cookie; /* expected immediate value (the cookie) */
};
/* Validates one work completion against the expectation; returns
* false and fills *reason on mismatch. */
bool validateDataCompletion(const struct ibv_wc& wc,
const WcExpectation& expect, const char** reason);
} // namespace v2
} // namespace hipObj