mirror of
https://github.com/versity/versitygw.git
synced 2026-09-22 16:04:15 +00:00
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>
249 lines
7.1 KiB
C++
249 lines
7.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
|
|
*/
|
|
|
|
#include "v2_session.h"
|
|
|
|
#include <utility>
|
|
|
|
namespace hipObj {
|
|
namespace v2 {
|
|
|
|
namespace {
|
|
|
|
void notifyAll(std::condition_variable& cv) {
|
|
cv.notify_all();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool SessionTable::insert(V2Session&& session) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
session.ioActive = 1; /* published together with the entry */
|
|
auto [it, ok] = entries_.emplace(session.id, std::move(session));
|
|
if (ok) {
|
|
notifyAll(cv_);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool SessionTable::beginPublishing(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end() || it->second.state != SessState::Prepared) {
|
|
return false;
|
|
}
|
|
it->second.state = SessState::Publishing;
|
|
/* Response transmission bound: 5s from confirmation. */
|
|
it->second.txDeadlineAt = clockSource().nowMs() + 5000;
|
|
notifyAll(cv_);
|
|
return true;
|
|
}
|
|
|
|
bool SessionTable::finishPublishing(const std::string& id, uint64_t tPrepMs) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end() || it->second.state != SessState::Publishing) {
|
|
return false;
|
|
}
|
|
it->second.state = SessState::Prepared;
|
|
it->second.clientDeadlineAt = clockSource().nowMs() + tPrepMs;
|
|
/* The PREPARE response left: its bound must stop applying so a
|
|
* READY acquiring a reference afterwards can never be hit by a
|
|
* stale forced release. */
|
|
it->second.txDeadlineAt = 0;
|
|
notifyAll(cv_);
|
|
return true;
|
|
}
|
|
|
|
bool SessionTable::beginTransferring(const std::string& id, uint64_t tExecMs) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end() || it->second.state != SessState::Prepared) {
|
|
return false;
|
|
}
|
|
/* Expiry check shares the lock: a deadline-passed session cannot
|
|
* be revived by a READY (single source of truth). */
|
|
if (clockSource().nowMs() > it->second.clientDeadlineAt) {
|
|
it->second.state = SessState::Reaping;
|
|
notifyAll(cv_);
|
|
return false;
|
|
}
|
|
it->second.state = SessState::Transferring;
|
|
it->second.clientDeadlineAt = clockSource().nowMs() + tExecMs;
|
|
/* The PREPARE response bound no longer applies; the FINAL
|
|
* response arms a fresh one at beginCompleting. Clearing here
|
|
* keeps the reaper's forced release from firing on a stale
|
|
* bound during the data phase. */
|
|
it->second.txDeadlineAt = 0;
|
|
notifyAll(cv_);
|
|
return true;
|
|
}
|
|
|
|
bool SessionTable::beginCompleting(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it != entries_.end() && it->second.state == SessState::Transferring) {
|
|
/* Fresh response bound for the FINAL transmission. */
|
|
it->second.txDeadlineAt = clockSource().nowMs() + 5000;
|
|
}
|
|
if (it == entries_.end() || it->second.state != SessState::Transferring) {
|
|
return false;
|
|
}
|
|
it->second.state = SessState::Completing;
|
|
/* Mark the reference origin atomically with the transition:
|
|
* from here the io reference guards live staging data and the
|
|
* reaper must not force-release it. Setting this outside the
|
|
* lock would leave a window where the reaper still treats the
|
|
* reference as a Publishing orphan. */
|
|
it->second.ioFromCompleting = true;
|
|
notifyAll(cv_);
|
|
return true;
|
|
}
|
|
|
|
bool SessionTable::toReaping(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end() || it->second.state == SessState::Reaping) {
|
|
return false;
|
|
}
|
|
it->second.state = SessState::Reaping;
|
|
notifyAll(cv_);
|
|
return true;
|
|
}
|
|
|
|
SessState SessionTable::awaitNotPublishing(const std::string& id,
|
|
uint64_t waitDeadlineMs) {
|
|
std::unique_lock<std::mutex> lock(mtx_);
|
|
const auto deadline = std::chrono::steady_clock::time_point(
|
|
std::chrono::milliseconds(waitDeadlineMs));
|
|
cv_.wait_until(lock, deadline, [&] {
|
|
auto it = entries_.find(id);
|
|
return it == entries_.end() || it->second.state != SessState::Publishing;
|
|
});
|
|
auto it = entries_.find(id);
|
|
return it == entries_.end() ? SessState::Reaping : it->second.state;
|
|
}
|
|
|
|
SessState SessionTable::stateOf(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
return it == entries_.end() ? SessState::Reaping : it->second.state;
|
|
}
|
|
|
|
bool SessionTable::claimDestroy(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end()) {
|
|
return false;
|
|
}
|
|
V2Session& s = it->second;
|
|
/* Active handler work is never preempted: the worker's
|
|
* finalizer performs the transition and re-enters the gate. */
|
|
if (s.state != SessState::Reaping || s.ioActive > 0 || s.destroying ||
|
|
(s.destroyClaimed && !s.poisoned)) {
|
|
return false;
|
|
}
|
|
s.destroying = true;
|
|
s.destroyClaimed = true;
|
|
return true;
|
|
}
|
|
|
|
void SessionTable::commitDestroy(const std::string& id, bool qpOk, bool cqOk) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end() || !it->second.destroying) {
|
|
return;
|
|
}
|
|
V2Session& s = it->second;
|
|
if (qpOk) {
|
|
s.qp = nullptr;
|
|
s.serverQpn = 0;
|
|
}
|
|
if (cqOk) {
|
|
s.cq = nullptr;
|
|
}
|
|
if (s.qp == nullptr && s.cq == nullptr) {
|
|
it->second.ioActive = 0;
|
|
entries_.erase(it);
|
|
notifyAll(cv_);
|
|
return;
|
|
}
|
|
s.poisoned = true;
|
|
s.destroying = false;
|
|
}
|
|
|
|
bool SessionTable::eraseSession(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end()) {
|
|
return false;
|
|
}
|
|
if (it->second.qp != nullptr || it->second.cq != nullptr) {
|
|
return false;
|
|
}
|
|
entries_.erase(it);
|
|
notifyAll(cv_);
|
|
return true;
|
|
}
|
|
|
|
size_t SessionTable::size() const {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
return entries_.size();
|
|
}
|
|
|
|
std::vector<std::string> SessionTable::ids() const {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
std::vector<std::string> out;
|
|
out.reserve(entries_.size());
|
|
for (const auto& [id, s] : entries_) {
|
|
out.push_back(id);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
uint64_t SessionTable::ringReserve() {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
return ring_.reserve();
|
|
}
|
|
|
|
void SessionTable::ringUnreserve(uint64_t reservationId) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
ring_.unreserve(reservationId);
|
|
}
|
|
|
|
void SessionTable::ringRecord(uint64_t reservationId, uint32_t qpn,
|
|
uint32_t psn) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
ring_.record(reservationId, qpn, psn);
|
|
}
|
|
|
|
void SessionTable::ringCollectExpired(uint64_t nowMs) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
ring_.collectExpired(nowMs);
|
|
}
|
|
|
|
bool SessionTable::acquireIo(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end()) {
|
|
return false;
|
|
}
|
|
++it->second.ioActive;
|
|
return true;
|
|
}
|
|
|
|
int SessionTable::releaseIo(const std::string& id) {
|
|
std::lock_guard<std::mutex> guard(mtx_);
|
|
auto it = entries_.find(id);
|
|
if (it == entries_.end() || it->second.ioActive <= 0) {
|
|
return -1;
|
|
}
|
|
return --it->second.ioActive;
|
|
}
|
|
|
|
} // namespace v2
|
|
} // namespace hipObj
|