173 lines
4.9 KiB
C++
173 lines
4.9 KiB
C++
// Copyright 2024 KVCache.AI
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#ifndef EFA_ENDPOINT_H
|
|
#define EFA_ENDPOINT_H
|
|
|
|
#include <glog/logging.h>
|
|
#include <rdma/fabric.h>
|
|
#include <rdma/fi_domain.h>
|
|
#include <rdma/fi_endpoint.h>
|
|
#include <rdma/fi_cm.h>
|
|
#include <rdma/fi_rma.h>
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <queue>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "common.h"
|
|
#include "efa_context.h"
|
|
#include "transfer_metadata.h"
|
|
#include "transport/transport.h"
|
|
|
|
namespace mooncake {
|
|
|
|
class EfaContext;
|
|
|
|
// Custom context for libfabric operations - stores slice pointer for completion
|
|
// handling This struct MUST have fi_context as its first member
|
|
struct EfaOpContext {
|
|
struct fi_context fi_ctx; // Must be first member
|
|
Transport::Slice* slice; // Slice pointer for completion handling
|
|
volatile int* wr_depth; // Pointer to endpoint's wr_depth_ for CQ
|
|
// completion decrement
|
|
};
|
|
|
|
// EfaEndPoint represents a libfabric endpoint for EFA communication.
|
|
// Unlike RDMA QPs, EFA uses RDM (Reliable Datagram) endpoints with
|
|
// an address vector for peer addressing.
|
|
class EfaEndPoint {
|
|
public:
|
|
using HandShakeDesc = TransferMetadata::HandShakeDesc;
|
|
|
|
enum Status { INITIALIZING, UNCONNECTED, CONNECTED };
|
|
|
|
EfaEndPoint(EfaContext& context);
|
|
~EfaEndPoint();
|
|
|
|
// Construct endpoint with specified completion queue
|
|
int construct(struct fid_cq* cq, volatile int* cq_outstanding,
|
|
size_t num_qp_list = 1, size_t max_sge = 4,
|
|
size_t max_wr = 256, size_t max_inline = 64);
|
|
|
|
private:
|
|
int deconstruct();
|
|
|
|
public:
|
|
void setPeerNicPath(const std::string& peer_nic_path);
|
|
|
|
int setupConnectionsByActive();
|
|
|
|
int setupConnectionsByActive(const std::string& peer_nic_path) {
|
|
setPeerNicPath(peer_nic_path);
|
|
return setupConnectionsByActive();
|
|
}
|
|
|
|
int setupConnectionsByPassive(const HandShakeDesc& peer_desc,
|
|
HandShakeDesc& local_desc);
|
|
|
|
bool hasOutstandingSlice() const;
|
|
|
|
bool active() const { return active_; }
|
|
|
|
void set_active(bool flag) {
|
|
RWSpinlock::WriteGuard guard(lock_);
|
|
active_ = flag;
|
|
if (!flag) inactive_time_ = getCurrentTimeInNano();
|
|
}
|
|
|
|
double inactiveTime() {
|
|
if (active_) return 0.0;
|
|
return (getCurrentTimeInNano() - inactive_time_) / 1000000000.0;
|
|
}
|
|
|
|
void touchLastUsed() { last_used_time_ = getCurrentTimeInNano(); }
|
|
|
|
double lastUsedAge() const {
|
|
return (getCurrentTimeInNano() - last_used_time_) / 1000000000.0;
|
|
}
|
|
|
|
public:
|
|
bool connected() const {
|
|
return status_.load(std::memory_order_relaxed) == CONNECTED;
|
|
}
|
|
|
|
void disconnect();
|
|
int destroyQP();
|
|
|
|
private:
|
|
void disconnectUnlocked();
|
|
|
|
public:
|
|
const std::string toString() const;
|
|
|
|
// Submit RDMA write/read operations via libfabric
|
|
int submitPostSend(std::vector<Transport::Slice*>& slice_list,
|
|
std::vector<Transport::Slice*>& failed_slice_list);
|
|
|
|
// Get the number of endpoints (always 1 for EFA RDM)
|
|
size_t getQPNumber() const { return 1; }
|
|
|
|
// Get local endpoint address for handshake
|
|
std::string getLocalAddr() const;
|
|
|
|
// Get peer's fi_addr
|
|
fi_addr_t getPeerFiAddr() const { return peer_fi_addr_; }
|
|
|
|
EfaContext& context() { return context_; }
|
|
|
|
private:
|
|
// Setup connection using peer's address from handshake
|
|
int doSetupConnection(const std::string& peer_addr,
|
|
std::string* reply_msg = nullptr);
|
|
|
|
// Insert peer address into address vector
|
|
int insertPeerAddr(const std::string& peer_addr);
|
|
|
|
private:
|
|
EfaContext& context_;
|
|
std::atomic<Status> status_;
|
|
|
|
RWSpinlock lock_;
|
|
std::string peer_nic_path_;
|
|
|
|
// Libfabric endpoint
|
|
struct fid_ep* ep_;
|
|
struct fid_cq* tx_cq_;
|
|
struct fid_cq* rx_cq_;
|
|
fi_addr_t peer_fi_addr_; // Peer's address in the AV
|
|
|
|
// Local endpoint address (for handshake)
|
|
std::vector<uint8_t> local_addr_;
|
|
size_t local_addr_len_;
|
|
|
|
volatile int wr_depth_;
|
|
int max_wr_depth_;
|
|
volatile int* cq_outstanding_;
|
|
|
|
// Spinlock to serialize fi_write/fi_read calls on this endpoint.
|
|
// libfabric RDM endpoints are not thread-safe by default.
|
|
std::atomic_flag post_lock_ = ATOMIC_FLAG_INIT;
|
|
|
|
volatile bool active_;
|
|
volatile uint64_t inactive_time_;
|
|
volatile uint64_t last_used_time_; // Updated on connection and I/O
|
|
};
|
|
|
|
} // namespace mooncake
|
|
|
|
#endif // EFA_ENDPOINT_H
|