2572 lines
93 KiB
C++
2572 lines
93 KiB
C++
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you 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.
|
|
|
|
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <gtest/gtest.h>
|
|
#include <gflags/gflags.h>
|
|
#if BRPC_WITH_RDMA
|
|
#include <google/protobuf/descriptor.h>
|
|
#include "butil/endpoint.h"
|
|
#include "butil/fd_guard.h"
|
|
#include "butil/iobuf.h"
|
|
#include "butil/sys_byteorder.h"
|
|
#include "butil/files/temp_file.h"
|
|
#include "brpc/acceptor.h"
|
|
#include "brpc/channel.h"
|
|
#include "brpc/controller.h"
|
|
#include "brpc/server.h"
|
|
#include "brpc/socket.h"
|
|
#include "brpc/errno.pb.h"
|
|
#include "brpc/parallel_channel.h"
|
|
#include "brpc/selective_channel.h"
|
|
#include "brpc/rdma_transport.h"
|
|
#include "brpc/rdma/block_pool.h"
|
|
#include "brpc/rdma/rdma_endpoint.h"
|
|
#include "brpc/rdma/rdma_handshake.h"
|
|
#include "brpc/rdma/rdma_handshake.pb.h"
|
|
#include "brpc/rdma/rdma_helper.h"
|
|
#include "echo.pb.h"
|
|
|
|
static const int PORT = 8713;
|
|
|
|
using namespace brpc;
|
|
|
|
namespace brpc {
|
|
|
|
DECLARE_int64(socket_max_unwritten_bytes);
|
|
DECLARE_bool(log_idle_connection_close);
|
|
DEFINE_bool(rdma_test_enable, false, "Enable tests requring rdma runtime.");
|
|
|
|
namespace rdma {
|
|
|
|
extern const uint16_t RDMA_HELLO_V2_VERSION;
|
|
extern const uint16_t RDMA_IMPL_V2_VERSION;
|
|
|
|
DECLARE_bool(rdma_trace_verbose);
|
|
DECLARE_int32(rdma_memory_pool_max_regions);
|
|
DECLARE_int32(rdma_client_handshake_version);
|
|
|
|
extern ibv_cq* (*IbvCreateCq)(ibv_context*, int, void*, ibv_comp_channel*, int);
|
|
extern int (*IbvDestroyCq)(ibv_cq*);
|
|
extern ibv_qp* (*IbvCreateQp)(ibv_pd*, ibv_qp_init_attr*);
|
|
extern int (*IbvModifyQp)(ibv_qp*, ibv_qp_attr*, ibv_qp_attr_mask);
|
|
extern int (*IbvQueryQp)(ibv_qp*, ibv_qp_attr*, ibv_qp_attr_mask, ibv_qp_init_attr*);
|
|
extern int (*IbvDestroyQp)(ibv_qp*);
|
|
extern butil::atomic<bool> g_rdma_available;
|
|
extern bool g_skip_rdma_init;
|
|
} // namespace rdma
|
|
} // namespace brpc
|
|
|
|
static std::string g_ip = "127.0.0.1";
|
|
static butil::EndPoint g_ep;
|
|
|
|
class MyEchoService : public ::test::EchoService {
|
|
void Echo(google::protobuf::RpcController* cntl_base,
|
|
const ::test::EchoRequest* req,
|
|
::test::EchoResponse* res,
|
|
google::protobuf::Closure* done) {
|
|
Controller* cntl = static_cast<Controller*>(cntl_base);
|
|
ClosureGuard done_guard(done);
|
|
if (req->server_fail()) {
|
|
cntl->SetFailed(req->server_fail(), "Server fail1");
|
|
cntl->SetFailed(req->server_fail(), "Server fail2");
|
|
return;
|
|
}
|
|
if (req->close_fd()) {
|
|
usleep(1);
|
|
LOG(INFO) << "close fd...";
|
|
cntl->CloseConnection("Close connection according to request");
|
|
return;
|
|
}
|
|
if (req->sleep_us() > 0) {
|
|
LOG(INFO) << "sleep " << req->sleep_us() << "us...";
|
|
bthread_usleep(req->sleep_us());
|
|
}
|
|
res->set_message("MyEchoService");
|
|
if (req->code() != 0) {
|
|
res->add_code_list(req->code());
|
|
}
|
|
cntl->response_attachment().append(cntl->request_attachment());
|
|
}
|
|
};
|
|
|
|
class RdmaTest : public ::testing::Test {
|
|
protected:
|
|
RdmaTest() {
|
|
butil::ip_t ip;
|
|
EXPECT_EQ(0, butil::str2ip(g_ip.c_str(), &ip));
|
|
butil::EndPoint ep(ip, PORT);
|
|
g_ep = ep;
|
|
EXPECT_EQ(0, _server_list.save(butil::endpoint2str(g_ep).c_str()));
|
|
_naming_url = std::string("File://") + _server_list.fname();
|
|
_server.AddService(&_svc, SERVER_DOESNT_OWN_SERVICE);
|
|
}
|
|
~RdmaTest() { }
|
|
|
|
virtual void SetUp() { }
|
|
|
|
virtual void TearDown() {
|
|
rdma::DumpMemoryPoolInfo(std::cout);
|
|
}
|
|
|
|
protected:
|
|
void StartServer(bool use_rdma = true) {
|
|
ServerOptions options;
|
|
options.enabled_protocols = "baidu_std";
|
|
options.socket_mode = use_rdma ? SOCKET_MODE_RDMA : SOCKET_MODE_TCP;
|
|
options.idle_timeout_sec = 5;
|
|
options.max_concurrency = 0;
|
|
options.internal_port = -1;
|
|
EXPECT_EQ(0, _server.Start(PORT, &options));
|
|
}
|
|
|
|
void StopServer() {
|
|
_server.Stop(0);
|
|
_server.Join();
|
|
}
|
|
|
|
Socket* GetSocketFromServer(size_t index) {
|
|
std::vector<SocketId> sids;
|
|
_server._am->ListConnections(&sids);
|
|
if (index >= sids.size()) {
|
|
return NULL;
|
|
}
|
|
SocketUniquePtr s;
|
|
if (Socket::Address(sids[index], &s) == 0) {
|
|
return s.get();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
butil::TempFile _server_list;
|
|
std::string _naming_url;
|
|
|
|
Server _server;
|
|
MyEchoService _svc;
|
|
};
|
|
|
|
// Parameterized fixture used by upper-layer RPC tests that have no
|
|
// dependency on the handshake wire format. The parameter is the
|
|
// client-side handshake protocol version (FLAGS_rdma_client_handshake_version),
|
|
// so every TEST_P below is automatically executed once per supported
|
|
// version. Add a new version to INSTANTIATE_TEST_SUITE_P at the bottom
|
|
// of this file and these RPC tests will gain coverage for free.
|
|
class RdmaRpcTest : public RdmaTest,
|
|
public ::testing::WithParamInterface<int> {
|
|
protected:
|
|
void SetUp() override {
|
|
RdmaTest::SetUp();
|
|
_saved_handshake_version = rdma::FLAGS_rdma_client_handshake_version;
|
|
rdma::FLAGS_rdma_client_handshake_version = GetParam();
|
|
}
|
|
void TearDown() override {
|
|
rdma::FLAGS_rdma_client_handshake_version = _saved_handshake_version;
|
|
RdmaTest::TearDown();
|
|
}
|
|
|
|
private:
|
|
int _saved_handshake_version = 2;
|
|
};
|
|
|
|
TEST_F(RdmaTest, client_close_before_hello_send) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_hello_msg_invalid_magic_str) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
memcpy(data, "PRPC", 4); // send as normal baidu_std protocol
|
|
ASSERT_EQ(4, write(sockfd, data, 4));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_close_during_hello_send) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
uint8_t data[8];
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RD", 2);
|
|
ASSERT_EQ(2, write(sockfd1, data, 2)); // break in magic str
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
butil::fd_guard sockfd2(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd2 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd2, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RDMA", 4);
|
|
ASSERT_EQ(4, write(sockfd2, data, 4)); // break after magic str
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd2);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
butil::fd_guard sockfd3(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd3 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd3, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RDMA", 4);
|
|
memset(data + 4, 0, 4);
|
|
ASSERT_EQ(8, write(sockfd3, data, 8)); // break after magic str
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd3);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_hello_msg_invalid_len) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RDMA", 4);
|
|
ASSERT_EQ(4, write(sockfd1, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memset(data + 4, 0, 36);
|
|
ASSERT_EQ(36, write(sockfd1, data + 4, 36)); // Write invalid length.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
butil::fd_guard sockfd2(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd2 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd2, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RDMA", 4);
|
|
ASSERT_EQ(4, write(sockfd2, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
uint16_t len = butil::HostToNet16(35);
|
|
memcpy(data + 4, &len, sizeof(len));
|
|
memset(data + 6, 0, 34);
|
|
ASSERT_EQ(36, write(sockfd2, data + 4, 36)); // write invalid length
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_hello_msg_invalid_version) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
uint16_t len = butil::HostToNet16(rdma::v2_wire::HELLO_MSG_LEN_MIN);
|
|
uint16_t ver = butil::HostToNet16(1);
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RDMA", 4);
|
|
ASSERT_EQ(4, write(sockfd1, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data + 4, &len, 2);
|
|
memset(data + 6, 0, 34);
|
|
memcpy(data + 6, &ver, 2); // hello_ver == 1, impl_ver == 0
|
|
// Write the 36B base starting at data + 4 (NOT data). Pre-Step-1 this
|
|
// UT mistakenly wrote `data, 36` which included the leftover "RDMA"
|
|
// magic at data[0..4); the server parsed it as msg_len = 0x5244 and
|
|
// happened to fall through to NegotiationValid (which then failed on
|
|
// hello_ver). Now that Step 1 enforces a HELLO_MSG_LEN_MAX upper bound,
|
|
// such an oversized msg_len would be rejected before reaching the
|
|
// version check, breaking the intent of this UT.
|
|
ASSERT_EQ(36, write(sockfd1, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
uint32_t flags = 0;
|
|
ASSERT_EQ(sizeof(flags), write(sockfd1, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
sockfd1.reset(-1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
butil::fd_guard sockfd2(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd2 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd2, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data, "RDMA", 4);
|
|
ASSERT_EQ(4, write(sockfd2, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
memcpy(data + 4, &len, 2);
|
|
memset(data + 6, 0, 32);
|
|
memcpy(data + 8, &ver, 2); // hello_ver == 0, impl_ver == 1
|
|
// See comment above on `write(sockfd1, data + 4, 36)` for why we
|
|
// write from data + 4 instead of data.
|
|
ASSERT_EQ(36, write(sockfd2, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd2, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
sockfd2.reset(-1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_hello_msg_invalid_sq_rq_block_size) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
uint32_t flags = butil::HostToNet32(0);
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
|
|
msg.sq_size = 10;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd1, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd1, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd1, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
sockfd1.reset(-1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 10;
|
|
msg.block_size = 8192;
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
butil::fd_guard sockfd2(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd2 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd2, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd2, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd2, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd2, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
sockfd2.reset(-1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 1000;
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
butil::fd_guard sockfd3(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd3 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd3, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd3, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd3, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd3, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
sockfd3.reset(-1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_close_after_qp_build) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(40, write(sockfd1, data, 40));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_close_during_ack_send) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd1, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd1, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
uint32_t flags = butil::HostToNet32(1);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd1, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_close_after_ack_send) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd1, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd1, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
uint32_t flags = butil::HostToNet32(0);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd1, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
close(sockfd1);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
butil::fd_guard sockfd2(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd2 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd2, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd2, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd2, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
flags = butil::HostToNet32(1);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd2, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
close(sockfd2);
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, client_send_data_on_tcp_after_ack_send) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
Socket* s = NULL;
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
|
|
butil::fd_guard sockfd1(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd1 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd1, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd1, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd1, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
uint32_t flags = butil::HostToNet32(0);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd1, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd1, &flags, sizeof(flags)));
|
|
usleep(100000);
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
butil::fd_guard sockfd2(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd2 >= 0);
|
|
ASSERT_EQ(0, connect(sockfd2, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, write(sockfd2, data, 4)); // Write magic string.
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(36, write(sockfd2, data + 4, 36));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
flags = butil::HostToNet32(1);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd2, &flags, sizeof(flags)));
|
|
usleep(100000); // wait for server to handle the msg
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd2, &flags, sizeof(flags)));
|
|
usleep(100000);
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_miss_before_hello_send) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(ERPCTIMEDOUT, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_close_before_hello_send) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
close(acc_fd);
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FAILED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EEOF, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_miss_during_magic_str) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
ASSERT_EQ(2, write(acc_fd, "RD", 2));
|
|
usleep(100000);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(ERPCTIMEDOUT, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_close_during_magic_str) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
ASSERT_EQ(2, write(acc_fd, "RD", 2));
|
|
usleep(100000);
|
|
close(acc_fd);
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FAILED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EEOF, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_hello_invalid_magic_str) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
ASSERT_EQ(4, write(acc_fd, "ABCD", 4));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FAILED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EPROTO, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_miss_during_hello_msg) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
ASSERT_EQ(4, write(acc_fd, "RDMA", 4));
|
|
ASSERT_EQ(2, write(acc_fd, "00", 2));
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(ERPCTIMEDOUT, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_close_during_hello_msg) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
ASSERT_EQ(4, write(acc_fd, "RDMA", 4));
|
|
ASSERT_EQ(2, write(acc_fd, "00", 2));
|
|
close(acc_fd);
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FAILED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EEOF, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_hello_invalid_msg_len) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
memcpy(data, "RDMA", 4);
|
|
uint16_t len = butil::HostToNet16(35);
|
|
memcpy(data + 4, &len, 2);
|
|
memset(data + 6, 0, 32);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FAILED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EPROTO, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_hello_invalid_version) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
memcpy(data, "RDMA", 4);
|
|
uint16_t len = butil::HostToNet16(rdma::v2_wire::HELLO_MSG_LEN_MIN);
|
|
memcpy(data + 4, &len, 2);
|
|
memset(data + 6, 0, 32);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, read(acc_fd, data, 4));
|
|
uint32_t* tmp = (uint32_t*)data;
|
|
ASSERT_EQ(0, butil::NetToHost32(*tmp));
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(ERPCTIMEDOUT, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_hello_invalid_sq_rq_size) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = 1;
|
|
msg.impl_ver = 1;
|
|
msg.sq_size = 0;
|
|
msg.rq_size = 0;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, read(acc_fd, data, 4));
|
|
uint32_t* tmp = (uint32_t*)data;
|
|
ASSERT_EQ(0, butil::NetToHost32(*tmp));
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(ERPCTIMEDOUT, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_miss_after_ack) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, read(acc_fd, data, 4));
|
|
uint32_t* tmp = (uint32_t*)data;
|
|
ASSERT_EQ(1, butil::NetToHost32(*tmp));
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(ERPCTIMEDOUT, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_close_after_ack) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(4, read(acc_fd, data, 4));
|
|
uint32_t* tmp = (uint32_t*)data;
|
|
ASSERT_EQ(1, butil::NetToHost32(*tmp));
|
|
close(acc_fd);
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EEOF, cntl.ErrorCode());
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_send_data_on_tcp_after_ack) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::C_HELLO_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
bthread_id_join(cntl.call_id());
|
|
|
|
ASSERT_EQ(EPROTO, cntl.ErrorCode());
|
|
}
|
|
|
|
|
|
TEST_F(RdmaTest, v2_client_hello_bytes_baseline) {
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(acc_fd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
// [0..4) magic
|
|
ASSERT_EQ(0, memcmp(data, "RDMA", 4));
|
|
// [4..6) msg_len, big-endian uint16 == 40
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN,
|
|
(size_t)(((uint16_t)data[4] << 8) | (uint16_t)data[5]));
|
|
// [6..8) hello_ver, big-endian uint16 == rdma::RDMA_HELLO_V2_VERSION
|
|
ASSERT_EQ(rdma::RDMA_HELLO_V2_VERSION,
|
|
(uint16_t)(((uint16_t)data[6] << 8) | (uint16_t)data[7]));
|
|
// [8..10) impl_ver, big-endian uint16 == rdma::RDMA_IMPL_V2_VERSION
|
|
ASSERT_EQ(rdma::RDMA_IMPL_V2_VERSION,
|
|
(uint16_t)(((uint16_t)data[8] << 8) | (uint16_t)data[9]));
|
|
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.Deserialize(data + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, msg.msg_len);
|
|
ASSERT_EQ(rdma::RDMA_HELLO_V2_VERSION, msg.hello_ver);
|
|
ASSERT_EQ(rdma::RDMA_IMPL_V2_VERSION, msg.impl_ver);
|
|
|
|
bthread_id_join(cntl.call_id());
|
|
}
|
|
|
|
TEST_F(RdmaTest, v2_server_hello_bytes_baseline) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
// Send a well-formed v2 hello so the server enters S_ACK_WAIT.
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = rdma::v2_wire::HELLO_MSG_LEN_MIN;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
|
|
uint8_t data[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
memcpy(data, "RDMA", 4);
|
|
msg.Serialize(data + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(sockfd, data, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
// Read server's reply hello and assert its byte-level layout.
|
|
uint8_t reply[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, read(sockfd, reply, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
|
|
ASSERT_EQ(0, memcmp(reply, "RDMA", 4));
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN,
|
|
(size_t)(((uint16_t)reply[4] << 8) | (uint16_t)reply[5]));
|
|
ASSERT_EQ(rdma::RDMA_HELLO_V2_VERSION,
|
|
(uint16_t)(((uint16_t)reply[6] << 8) | (uint16_t)reply[7]));
|
|
ASSERT_EQ(rdma::RDMA_IMPL_V2_VERSION,
|
|
(uint16_t)(((uint16_t)reply[8] << 8) | (uint16_t)reply[9]));
|
|
|
|
rdma::v2_wire::HelloMessage reply_msg{};
|
|
reply_msg.Deserialize(reply + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, reply_msg.msg_len);
|
|
ASSERT_EQ(rdma::RDMA_HELLO_V2_VERSION, reply_msg.hello_ver);
|
|
ASSERT_EQ(rdma::RDMA_IMPL_V2_VERSION, reply_msg.impl_ver);
|
|
|
|
// Drive the server into FALLBACK_TCP via ACK flags=0 so the test ends
|
|
// cleanly without requiring real RDMA hardware.
|
|
uint32_t flags = butil::HostToNet32(0);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd, &flags, sizeof(flags)));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
sockfd.reset(-1);
|
|
usleep(100000);
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, v2_server_drains_tail_then_reads_ack) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_TRUE(s != NULL);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
// Build a v2 hello with msg_len = 48 (40 base + 8B zero tail).
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = 48;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
|
|
uint8_t buf[48];
|
|
memcpy(buf, "RDMA", 4);
|
|
msg.Serialize(buf + 4);
|
|
memset(buf + 40, 0x00, 8); // 8B zero tail
|
|
ASSERT_EQ(48, write(sockfd, buf, 48));
|
|
usleep(100000);
|
|
|
|
// Send the real ACK (flags=1 = ACK_MSG_RDMA_OK).
|
|
uint32_t flags = butil::HostToNet32(1);
|
|
ASSERT_EQ(sizeof(flags), write(sockfd, &flags, sizeof(flags)));
|
|
usleep(100000);
|
|
|
|
ASSERT_EQ(rdma::RdmaEndpoint::ESTABLISHED, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
sockfd.reset(-1);
|
|
usleep(100000);
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, v2_server_rejects_oversized_msg_len) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_TRUE(s != NULL);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
// Build a v2 hello with msg_len = 4097 (HELLO_MSG_LEN_MAX + 1).
|
|
// We only send the 40B base; the server must reject before reading
|
|
// (and definitely before attempting to drain) any "tail".
|
|
rdma::v2_wire::HelloMessage msg{};
|
|
msg.msg_len = 4097;
|
|
msg.hello_ver = rdma::RDMA_HELLO_V2_VERSION;
|
|
msg.impl_ver = rdma::RDMA_IMPL_V2_VERSION;
|
|
msg.sq_size = 16;
|
|
msg.rq_size = 16;
|
|
msg.block_size = 8192;
|
|
msg.qp_num = 0;
|
|
msg.gid = rdma::GetRdmaGid();
|
|
|
|
uint8_t buf[rdma::v2_wire::HELLO_MSG_LEN_MIN];
|
|
memcpy(buf, "RDMA", 4);
|
|
msg.Serialize(buf + 4);
|
|
ASSERT_EQ(rdma::v2_wire::HELLO_MSG_LEN_MIN, write(sockfd, buf, rdma::v2_wire::HELLO_MSG_LEN_MIN));
|
|
usleep(100000);
|
|
|
|
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
sockfd.reset(-1);
|
|
usleep(100000);
|
|
|
|
StopServer();
|
|
}
|
|
|
|
// RAII for FLAGS_rdma_client_handshake_version: lets us flip the
|
|
// client-side handshake version for a single test and restore it on
|
|
// scope exit so subsequent tests stay on the v2 default.
|
|
class HandshakeVersionFlag {
|
|
public:
|
|
explicit HandshakeVersionFlag(int v)
|
|
: _saved(rdma::FLAGS_rdma_client_handshake_version) {
|
|
rdma::FLAGS_rdma_client_handshake_version = v;
|
|
}
|
|
~HandshakeVersionFlag() {
|
|
rdma::FLAGS_rdma_client_handshake_version = _saved;
|
|
}
|
|
private:
|
|
int _saved;
|
|
};
|
|
|
|
// Build a v3 wire packet from an RdmaHello: "RDM3" + pb_size_be + body.
|
|
std::string MakeV3Packet(const rdma::RdmaHello& msg) {
|
|
std::string body;
|
|
EXPECT_TRUE(msg.SerializeToString(&body));
|
|
std::string packet;
|
|
packet.reserve(4 + 4 + body.size());
|
|
packet.append("RDM3", 4);
|
|
uint32_t pb_size_be =
|
|
butil::HostToNet32(static_cast<uint32_t>(body.size()));
|
|
packet.append(reinterpret_cast<const char*>(&pb_size_be), 4);
|
|
packet.append(body);
|
|
return packet;
|
|
}
|
|
|
|
// Build a fully-valid RdmaHello: all 6 required fields are set, with
|
|
// values that pass RdmaHelloV3Wire::RdmaHelloValid().
|
|
// - block_size = 8192 (>= MIN_BLOCK_SIZE)
|
|
// - sq_size / rq_size = 16 (>= MIN_QP_SIZE)
|
|
// - gid = exactly 16B (sizeof(ibv_gid))
|
|
// - qp_num = 0 (allowed because g_skip_rdma_init in UT)
|
|
rdma::RdmaHello MakeValidV3Hello() {
|
|
rdma::RdmaHello msg;
|
|
msg.set_block_size(8192);
|
|
msg.set_sq_size(16);
|
|
msg.set_rq_size(16);
|
|
msg.set_lid(0);
|
|
ibv_gid gid = rdma::GetRdmaGid();
|
|
msg.set_gid(std::string(reinterpret_cast<const char*>(gid.raw),
|
|
sizeof(gid.raw)));
|
|
msg.set_qp_num(0);
|
|
return msg;
|
|
}
|
|
|
|
|
|
TEST_F(RdmaTest, v3_client_hello_bytes_baseline) {
|
|
HandshakeVersionFlag _hsv(3);
|
|
|
|
butil::fd_guard sockfd(butil::tcp_listen(g_ep));
|
|
EXPECT_TRUE(sockfd >= 0);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
butil::fd_guard acc_fd(accept(sockfd, NULL, NULL));
|
|
ASSERT_TRUE(acc_fd >= 0);
|
|
|
|
// [0..4) magic "RDM3"
|
|
uint8_t magic[4];
|
|
ASSERT_EQ(4, read(acc_fd, magic, 4));
|
|
ASSERT_EQ(0, memcmp(magic, "RDM3", 4));
|
|
|
|
// [4..8) pb_size, big-endian uint32, must be in (0, 4096]
|
|
uint8_t size_buf[4];
|
|
ASSERT_EQ(4, read(acc_fd, size_buf, 4));
|
|
uint32_t pb_size =
|
|
butil::NetToHost32(*reinterpret_cast<uint32_t*>(size_buf));
|
|
ASSERT_GT(pb_size, 0u);
|
|
ASSERT_LE(pb_size, 4096u);
|
|
|
|
// [8..8+pb_size) RdmaHello protobuf body.
|
|
std::string body(pb_size, '\0');
|
|
ASSERT_EQ((ssize_t)pb_size, read(acc_fd, &body[0], pb_size));
|
|
rdma::RdmaHello msg;
|
|
ASSERT_TRUE(msg.ParseFromString(body));
|
|
|
|
// All 6 required fields must be present (ParseFromString would
|
|
// have already returned false otherwise).
|
|
ASSERT_TRUE(msg.has_block_size());
|
|
ASSERT_TRUE(msg.has_sq_size());
|
|
ASSERT_TRUE(msg.has_rq_size());
|
|
ASSERT_TRUE(msg.has_lid());
|
|
ASSERT_TRUE(msg.has_gid());
|
|
ASSERT_TRUE(msg.has_qp_num());
|
|
// gid wire encoding must be exactly 16 bytes (sizeof(ibv_gid)).
|
|
ASSERT_EQ(sizeof(ibv_gid), msg.gid().size());
|
|
|
|
// Let the RPC time out and release resources.
|
|
bthread_id_join(cntl.call_id());
|
|
}
|
|
|
|
TEST_F(RdmaTest, v3_server_hello_bytes_baseline) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::UNINIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
// Send a valid v3 hello.
|
|
std::string packet = MakeV3Packet(MakeValidV3Hello());
|
|
ASSERT_EQ((ssize_t)packet.size(),
|
|
write(sockfd, packet.data(), packet.size()));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
// Read server's reply hello: 4B magic + 4B pb_size + body.
|
|
uint8_t reply_magic[4];
|
|
ASSERT_EQ(4, read(sockfd, reply_magic, 4));
|
|
ASSERT_EQ(0, memcmp(reply_magic, "RDM3", 4));
|
|
|
|
uint8_t size_buf[4];
|
|
ASSERT_EQ(4, read(sockfd, size_buf, 4));
|
|
uint32_t pb_size =
|
|
butil::NetToHost32(*reinterpret_cast<uint32_t*>(size_buf));
|
|
ASSERT_GT(pb_size, 0u);
|
|
ASSERT_LE(pb_size, 4096u);
|
|
|
|
std::string body(pb_size, '\0');
|
|
ASSERT_EQ((ssize_t)pb_size, read(sockfd, &body[0], pb_size));
|
|
rdma::RdmaHello reply;
|
|
ASSERT_TRUE(reply.ParseFromString(body));
|
|
ASSERT_TRUE(reply.has_block_size());
|
|
ASSERT_TRUE(reply.has_sq_size());
|
|
ASSERT_TRUE(reply.has_rq_size());
|
|
ASSERT_TRUE(reply.has_gid());
|
|
ASSERT_EQ(sizeof(ibv_gid), reply.gid().size());
|
|
|
|
// Drive the server into FALLBACK_TCP via ACK flags=0 so the test ends
|
|
// cleanly without requiring real RDMA hardware.
|
|
uint32_t flags = butil::HostToNet32(0);
|
|
ASSERT_EQ((ssize_t)sizeof(flags),
|
|
write(sockfd, &flags, sizeof(flags)));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
sockfd.reset(-1);
|
|
usleep(100000);
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, v3_server_rejects_zero_pb_size) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_TRUE(s != NULL);
|
|
|
|
// "RDM3" + pb_size = 0 (4B big-endian zero).
|
|
uint8_t buf[8] = {'R', 'D', 'M', '3', 0, 0, 0, 0};
|
|
ASSERT_EQ(8, write(sockfd, buf, 8));
|
|
usleep(100000);
|
|
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
sockfd.reset(-1);
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, v3_server_rejects_oversized_pb_size) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_TRUE(s != NULL);
|
|
|
|
uint8_t buf[8];
|
|
memcpy(buf, "RDM3", 4);
|
|
uint32_t pb_size_be = butil::HostToNet32(4097);
|
|
memcpy(buf + 4, &pb_size_be, 4);
|
|
ASSERT_EQ(8, write(sockfd, buf, 8));
|
|
usleep(100000);
|
|
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
sockfd.reset(-1);
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, v3_server_rejects_invalid_pb_bytes) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_TRUE(s != NULL);
|
|
|
|
// "RDM3" + pb_size = 8 + 8 bytes of 0xff (invalid protobuf body).
|
|
uint8_t buf[16];
|
|
memcpy(buf, "RDM3", 4);
|
|
uint32_t pb_size_be = butil::HostToNet32(8);
|
|
memcpy(buf + 4, &pb_size_be, 4);
|
|
memset(buf + 8, 0xff, 8);
|
|
ASSERT_EQ(16, write(sockfd, buf, 16));
|
|
usleep(100000);
|
|
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
sockfd.reset(-1);
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, v3_server_invalid_sq_size_falls_back) {
|
|
StartServer();
|
|
|
|
sockaddr_in addr;
|
|
bzero((char*)&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0));
|
|
ASSERT_TRUE(sockfd >= 0);
|
|
ASSERT_EQ(0, connect(sockfd, (sockaddr*)&addr, sizeof(sockaddr)));
|
|
usleep(100000);
|
|
Socket* s = GetSocketFromServer(0);
|
|
ASSERT_TRUE(s != NULL);
|
|
|
|
rdma::RdmaHello msg = MakeValidV3Hello();
|
|
msg.set_sq_size(0); // invalid: < MIN_QP_SIZE (16)
|
|
std::string packet = MakeV3Packet(msg);
|
|
ASSERT_EQ((ssize_t)packet.size(),
|
|
write(sockfd, packet.data(), packet.size()));
|
|
usleep(100000);
|
|
|
|
// Server validated the hello as invalid -> _rdma_state = RDMA_OFF,
|
|
// but still proceeds to S_ACK_WAIT (sends its own reply hello).
|
|
ASSERT_EQ(rdma::RdmaEndpoint::S_ACK_WAIT, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
ASSERT_EQ(RdmaTransport::RDMA_OFF, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_state);
|
|
|
|
// Drain server's reply hello (content not asserted here; covered
|
|
// by v3_server_hello_bytes_baseline).
|
|
uint8_t reply_hdr[8];
|
|
ASSERT_EQ(8, read(sockfd, reply_hdr, 8));
|
|
ASSERT_EQ(0, memcmp(reply_hdr, "RDM3", 4));
|
|
uint32_t reply_pb_size = butil::NetToHost32(
|
|
*reinterpret_cast<uint32_t*>(reply_hdr + 4));
|
|
std::string reply_body(reply_pb_size, '\0');
|
|
ASSERT_EQ((ssize_t)reply_pb_size,
|
|
read(sockfd, &reply_body[0], reply_pb_size));
|
|
|
|
// Client ACK flags=0 -> server settles into FALLBACK_TCP.
|
|
uint32_t flags = butil::HostToNet32(0);
|
|
ASSERT_EQ((ssize_t)sizeof(flags),
|
|
write(sockfd, &flags, sizeof(flags)));
|
|
usleep(100000);
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
|
|
sockfd.reset(-1);
|
|
usleep(100000);
|
|
ASSERT_EQ(NULL, GetSocketFromServer(0));
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_F(RdmaTest, try_global_disable_rdma) {
|
|
StartServer();
|
|
rdma::g_rdma_available.store(false, butil::memory_order_relaxed);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
|
|
req.set_message(__FUNCTION__);
|
|
req.set_sleep_us(200000);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
usleep(100000);
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ASSERT_EQ(rdma::RdmaEndpoint::FALLBACK_TCP, static_cast<RdmaTransport*>(s->_transport.get())->_rdma_ep->_state);
|
|
bthread_id_join(cntl.call_id());
|
|
ASSERT_EQ(0, cntl.ErrorCode());
|
|
|
|
StopServer();
|
|
rdma::g_rdma_available.store(true, butil::memory_order_relaxed);
|
|
}
|
|
|
|
TEST_F(RdmaTest, server_option_invalid) {
|
|
Server server;
|
|
ServerOptions options;
|
|
options.socket_mode = SOCKET_MODE_RDMA;
|
|
|
|
// rtmp and rdma are incompatible
|
|
options.rtmp_service = (RtmpService*)1;
|
|
ASSERT_EQ(-1, server.Start(PORT, &options));
|
|
|
|
// nshead and rdma are incompatible
|
|
options.rtmp_service = NULL;
|
|
options.nshead_service = (NsheadService*)1;
|
|
ASSERT_EQ(-1, server.Start(PORT, &options));
|
|
|
|
// mongo and rdma are incompatible
|
|
options.nshead_service = NULL;
|
|
options.mongo_service_adaptor = (MongoServiceAdaptor*)1;
|
|
ASSERT_EQ(-1, server.Start(PORT, &options));
|
|
|
|
// ssl and rdma are incompatible
|
|
options.mongo_service_adaptor = NULL;
|
|
options.mutable_ssl_options()->default_cert.certificate = "test";
|
|
ASSERT_EQ(-1, server.Start(PORT, &options));
|
|
}
|
|
|
|
TEST_F(RdmaTest, channel_option_invalid) {
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
|
|
// rtmp and rdma are incompatible
|
|
chan_options.protocol = "rtmp";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
chan_options.protocol = "streaming_rpc";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// nshead and rdma are incompatible
|
|
chan_options.protocol = "nshead";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
chan_options.protocol = "nshead_mcpack";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// nova_pbrpc and rdma are incompatible
|
|
chan_options.protocol = "nova_pbrpc";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// public_pbrpc and rdma are incompatible
|
|
chan_options.protocol = "public_pbrpc";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// redis and rdma are incompatible
|
|
chan_options.protocol = "redis";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// memcache and rdma are incompatible
|
|
chan_options.protocol = "memcache";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// ubrpc and rdma are incompatible
|
|
chan_options.protocol = "ubrpc_compack";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// itp and rdma are incompatible
|
|
chan_options.protocol = "itp";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// esp and rdma are incompatible
|
|
chan_options.protocol = "esp";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// hulu_pbrpc and rdma are incompatible
|
|
chan_options.protocol = "hulu_pbrpc";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// sofa_pbrpc and rdma are incompatible
|
|
chan_options.protocol = "sofa_pbrpc";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// http and rdma are incompatible
|
|
chan_options.protocol = "http";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
|
|
// ssl and rdma are incompatible
|
|
chan_options.protocol = "baidu_std";
|
|
chan_options.mutable_ssl_options()->sni_name = "test";
|
|
ASSERT_EQ(-1, channel.Init(g_ep, &chan_options));
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, rdma_client_to_rdma_server) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
// usleep(100000);
|
|
bthread_id_join(cntl.call_id());
|
|
ASSERT_EQ(0, cntl.ErrorCode());
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, tcp_client_to_tcp_server) {
|
|
StartServer(false);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
usleep(100000);
|
|
bthread_id_join(cntl.call_id());
|
|
ASSERT_EQ(0, cntl.ErrorCode());
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, tcp_client_to_rdma_server) {
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
usleep(100000);
|
|
bthread_id_join(cntl.call_id());
|
|
ASSERT_EQ(0, cntl.ErrorCode());
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, rdma_client_to_tcp_server) {
|
|
StartServer(false);
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
usleep(100000);
|
|
bthread_id_join(cntl.call_id());
|
|
ASSERT_EQ(EEOF, cntl.ErrorCode());
|
|
|
|
StopServer();
|
|
}
|
|
|
|
static const int RPC_NUM = 1024;
|
|
|
|
void DumpRdmaEndpointInfo(Socket* client, Socket* server) {
|
|
std::cout << std::endl << "client:";
|
|
static_cast<RdmaTransport*>(client->_transport.get())->_rdma_ep->DebugInfo(std::cout);
|
|
std::cout << std::endl << "server:";
|
|
static_cast<RdmaTransport*>(server->_transport.get())->_rdma_ep->DebugInfo(std::cout);
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, send_rpcs_in_one_qp) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 50000;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
LOG(INFO) << "send 0 attachment";
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (cntl[i].ErrorCode() == ERPCTIMEDOUT) {
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl[i]._single_server_id, &s));
|
|
Socket* m = GetSocketFromServer(0);
|
|
DumpRdmaEndpointInfo(s.get(), m);
|
|
}
|
|
ASSERT_EQ(0, cntl[i].ErrorCode()) << "req[" << i << "]";
|
|
}
|
|
|
|
LOG(INFO) << "send 4KB attachment";
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
cntl[i].Reset();
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (cntl[i].ErrorCode() == ERPCTIMEDOUT) {
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl[i]._single_server_id, &s));
|
|
Socket* m = GetSocketFromServer(0);
|
|
DumpRdmaEndpointInfo(s.get(), m);
|
|
}
|
|
ASSERT_EQ(0, cntl[i].ErrorCode()) << "req[" << i << "]";
|
|
}
|
|
|
|
LOG(INFO) << "send 1MB attachment";
|
|
attach.resize(1048576);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
cntl[i].Reset();
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (cntl[i].ErrorCode() == ERPCTIMEDOUT) {
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl[i]._single_server_id, &s));
|
|
Socket* m = GetSocketFromServer(0);
|
|
DumpRdmaEndpointInfo(s.get(), m);
|
|
}
|
|
ASSERT_TRUE(0 == cntl[i].ErrorCode() ||
|
|
EOVERCROWDED == cntl[i].ErrorCode()) << "req[" << i << "] " << berror(cntl[i].ErrorCode());
|
|
}
|
|
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl[0]._single_server_id, &s));
|
|
Socket* m = GetSocketFromServer(0);
|
|
DumpRdmaEndpointInfo(s.get(), m);
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, send_rpc_in_many_qp) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
butil::ip_t ip;
|
|
ASSERT_EQ(0, butil::str2ip(g_ip.c_str(), &ip));
|
|
|
|
Server server[100];
|
|
MyEchoService svc[100];
|
|
int num = 100;
|
|
butil::EndPoint server_eps[100];
|
|
for (int i = 0; i < num; ++i) {
|
|
ServerOptions options;
|
|
options.socket_mode = SOCKET_MODE_RDMA;
|
|
options.idle_timeout_sec = 1;
|
|
options.max_concurrency = 0;
|
|
options.internal_port = -1;
|
|
server[i].AddService(&svc[i], SERVER_DOESNT_OWN_SERVICE);
|
|
ASSERT_EQ(0, server[i].Start(0, &options));
|
|
server_eps[i] = butil::EndPoint(ip, server[i].listen_address().port);
|
|
}
|
|
|
|
int port = 0;
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 100000;
|
|
chan_options.max_retry = 0;
|
|
Channel channel[RPC_NUM];
|
|
Server* svr[RPC_NUM];
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
svr[i] = &server[i % num];
|
|
ASSERT_EQ(0, channel[i].Init(server_eps[(port++) % num], &chan_options));
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel[i]).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (cntl[i].ErrorCode() == ERPCTIMEDOUT) {
|
|
SocketUniquePtr s;
|
|
EXPECT_EQ(0, Socket::Address(cntl[i]._single_server_id, &s));
|
|
if (s && svr[i] && svr[i]->_am) {
|
|
std::vector<SocketId> sids;
|
|
svr[i]->_am->ListConnections(&sids);
|
|
for (size_t j = 0; j < sids.size(); ++j) {
|
|
SocketUniquePtr m;
|
|
if (Socket::AddressFailedAsWell(sids[j], &m) == 0) {
|
|
DumpRdmaEndpointInfo(s.get(), m.get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EXPECT_EQ(0, cntl[i].ErrorCode()) << "req[" << i << "]";
|
|
}
|
|
|
|
for (int i = 0; i < num; ++i) {
|
|
server[i].Stop(0);
|
|
server[i].Join();
|
|
}
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, send_rpcs_as_pooled_connection) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 30000; // it may very slow
|
|
chan_options.timeout_ms = 30000;
|
|
chan_options.max_retry = 0;
|
|
chan_options.connection_type = "pooled";
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (cntl[i].ErrorCode() == ERPCTIMEDOUT) {
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl[i]._single_server_id, &s));
|
|
Socket* m = GetSocketFromServer(0);
|
|
DumpRdmaEndpointInfo(s.get(), m);
|
|
}
|
|
ASSERT_EQ(0, cntl[i].ErrorCode()) << "req[" << i << "]";
|
|
}
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, send_rpcs_as_short_connection) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 30000; // it may very slow
|
|
chan_options.timeout_ms = 30000;
|
|
chan_options.max_retry = 0;
|
|
chan_options.connection_type = "short";
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (cntl[i].ErrorCode() == ERPCTIMEDOUT) {
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl[i]._single_server_id, &s));
|
|
Socket* m = GetSocketFromServer(0);
|
|
DumpRdmaEndpointInfo(s.get(), m);
|
|
}
|
|
ASSERT_EQ(0, cntl[i].ErrorCode()) << "req[" << i << "]";
|
|
}
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, server_stop_during_rpc) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 3000;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
if (i == 0) StopServer();
|
|
int error_code = cntl[i].ErrorCode();
|
|
ASSERT_TRUE(error_code == 0 ||
|
|
error_code == EEOF ||
|
|
error_code == ELOGOFF ||
|
|
error_code == EHOSTDOWN) << "req[" << i << "]: " << error_code;
|
|
}
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, server_close_during_rpc) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 3000;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(attach);
|
|
if (i == RPC_NUM / 2) {
|
|
req[i].set_close_fd(true);
|
|
}
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
int error_code = cntl[i].ErrorCode();
|
|
ASSERT_TRUE(error_code == 0 ||
|
|
error_code == EEOF ||
|
|
error_code == EFAILEDSOCKET ||
|
|
error_code == EHOSTDOWN) << "req[" << i << "]: " << error_code;
|
|
}
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, client_close_during_rpc) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 3000;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf attach;
|
|
attach.resize(4096);
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
|
|
cntl[0].CloseConnection("Close connection");
|
|
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
int error_code = cntl[i].ErrorCode();
|
|
ASSERT_TRUE(error_code == 0 ||
|
|
error_code == ECLOSE ||
|
|
error_code == EHOSTDOWN) << "req[" << i << "]: " << error_code;
|
|
}
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, verbs_error_handling) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
req.set_sleep_us(200000);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, done);
|
|
|
|
usleep(100000); // wait for rdma handshake complete
|
|
|
|
SocketUniquePtr s;
|
|
ASSERT_EQ(0, Socket::Address(cntl._single_server_id, &s));
|
|
ibv_send_wr wr;
|
|
memset(&wr, 0, sizeof(wr));
|
|
ibv_sge sge;
|
|
void* buf = malloc(8192);
|
|
sge.addr = (uint64_t)buf;
|
|
sge.length = 8192;
|
|
sge.lkey = 1; // incorrect lkey
|
|
wr.sg_list = &sge;
|
|
wr.num_sge = 1;
|
|
ibv_send_wr* bad = NULL;
|
|
auto rdma_transport = static_cast<RdmaTransport*>(s->_transport.get());
|
|
ibv_post_send(rdma_transport->_rdma_ep->_resource->qp, &wr, &bad);
|
|
bthread_id_join(cntl.call_id());
|
|
ASSERT_EQ(ERDMA, cntl.ErrorCode());
|
|
free(buf);
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, rdma_use_parallel_channel) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
const size_t NCHANS = 8;
|
|
Channel subchans[NCHANS];
|
|
ParallelChannel channel;
|
|
ChannelOptions opts;
|
|
opts.socket_mode = SOCKET_MODE_RDMA;
|
|
for (size_t i = 0; i < NCHANS; ++i) {
|
|
ASSERT_EQ(0, subchans[i].Init(_naming_url.c_str(), "rR", &opts));
|
|
ASSERT_EQ(0, channel.AddChannel(
|
|
&subchans[i], DOESNT_OWN_CHANNEL,
|
|
NULL, NULL));
|
|
}
|
|
ASSERT_EQ(0, channel.Init(NULL));
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, NULL);
|
|
|
|
ASSERT_EQ(0, cntl.ErrorCode());
|
|
ASSERT_EQ(NCHANS, (size_t)cntl.sub_count());
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, rdma_use_selective_channel) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
const size_t NCHANS = 8;
|
|
SelectiveChannel channel;
|
|
ChannelOptions opts;
|
|
opts.socket_mode = SOCKET_MODE_RDMA;
|
|
ASSERT_EQ(0, channel.Init("rr", &opts));
|
|
for (size_t i = 0; i < NCHANS; ++i) {
|
|
Channel* subchan = new Channel;
|
|
ASSERT_EQ(0, subchan->Init(_naming_url.c_str(), "rR", &opts));
|
|
ASSERT_EQ(0, channel.AddChannel(subchan, NULL));
|
|
}
|
|
|
|
Controller cntl;
|
|
test::EchoRequest req;
|
|
test::EchoResponse res;
|
|
req.set_message(__FUNCTION__);
|
|
::test::EchoService::Stub(&channel).Echo(&cntl, &req, &res, NULL);
|
|
|
|
ASSERT_EQ(0, cntl.ErrorCode()) << cntl.ErrorText();
|
|
ASSERT_EQ(1, cntl.sub_count());
|
|
|
|
StopServer();
|
|
}
|
|
|
|
static void MockFree(void* buf) { }
|
|
|
|
TEST_P(RdmaRpcTest, send_rpcs_with_user_defined_iobuf) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 500;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf attach;
|
|
void* data = malloc(4096);;
|
|
attach.append_user_data(data, 4096, NULL);
|
|
req[0].set_message(__FUNCTION__);
|
|
cntl[0].request_attachment().append(attach);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[0], &req[0], &res[0], done);
|
|
bthread_id_join(cntl[0].call_id());
|
|
ASSERT_EQ(ERDMAMEM, cntl[0].ErrorCode());
|
|
attach.clear();
|
|
sleep(2); // wait for client recover from EHOSTDOWN
|
|
cntl[0].Reset();
|
|
|
|
char* mr[2 * RPC_NUM];
|
|
uint32_t lkey[2 * RPC_NUM];
|
|
for (size_t i = 0; i < RPC_NUM; ++i) {
|
|
mr[2 * i] = (char*)malloc(4096);
|
|
memset(mr[2 * i], i % 100, 4096);
|
|
lkey[2 * i] = rdma::RegisterMemoryForRdma(mr[2 * i], 4096);
|
|
ASSERT_TRUE(lkey[2 * i] != 0);
|
|
cntl[i].request_attachment().append_user_data_with_meta(mr[2 * i] + i, 4096 - i, MockFree, lkey[2 * i]);
|
|
mr[2 * i + 1] = (char*)malloc(4096);
|
|
memset(mr[2 * i + 1], i % 100, 4096);
|
|
lkey[2 * i + 1] = rdma::RegisterMemoryForRdma(mr[2 * i + 1], 4096);
|
|
ASSERT_TRUE(lkey[2 * i + 1] != 0);
|
|
cntl[i].request_attachment().append_user_data_with_meta(mr[2 * i + 1] + i, 4096 - i, MockFree, lkey[2 * i + 1]);
|
|
req[i].set_message(__FUNCTION__);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (size_t i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
ASSERT_EQ(0, cntl[i].ErrorCode()) << "req[" << i << "]";
|
|
rdma::DeregisterMemoryForRdma(mr[i]);
|
|
ASSERT_EQ(2 * (4096 - i), cntl[i].response_attachment().size());
|
|
char tmp[8192];
|
|
cntl[i].response_attachment().copy_to(tmp, 2 * (4096 - i));
|
|
ASSERT_EQ(0, memcmp(mr[2 * i] + i, tmp, 4096 - i));
|
|
ASSERT_EQ(0, memcmp(mr[2 * i + 1] + i, tmp + 4096 - i, 4096 - i));
|
|
free(mr[2 * i]);
|
|
free(mr[2 * i + 1]);
|
|
}
|
|
|
|
StopServer();
|
|
}
|
|
|
|
TEST_P(RdmaRpcTest, try_memory_pool_empty) {
|
|
if (!FLAGS_rdma_test_enable) {
|
|
return;
|
|
}
|
|
|
|
StartServer();
|
|
|
|
Channel channel;
|
|
ChannelOptions chan_options;
|
|
chan_options.socket_mode = SOCKET_MODE_RDMA;
|
|
chan_options.connect_timeout_ms = 500;
|
|
chan_options.timeout_ms = 60000;
|
|
chan_options.max_retry = 0;
|
|
ASSERT_EQ(0, channel.Init(g_ep, &chan_options));
|
|
Controller cntl[RPC_NUM];
|
|
test::EchoRequest req[RPC_NUM];
|
|
test::EchoResponse res[RPC_NUM];
|
|
|
|
butil::IOBuf iobuf[RPC_NUM];
|
|
for (int i = 0; i < 1024; ++i) {
|
|
if (iobuf[i].resize(1048576 * 8)) {
|
|
// 8MB for each iobuf
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
req[i].set_message(__FUNCTION__);
|
|
cntl[i].request_attachment().append(iobuf[i]);
|
|
google::protobuf::Closure* done = DoNothing();
|
|
::test::EchoService::Stub(&channel).Echo(&cntl[i], &req[i], &res[i], done);
|
|
}
|
|
for (int i = 0; i < RPC_NUM; ++i) {
|
|
bthread_id_join(cntl[i].call_id());
|
|
}
|
|
|
|
StopServer();
|
|
}
|
|
|
|
// Run every TEST_P(RdmaRpcTest, ...) above twice: once with the
|
|
// client-side handshake forced to v2 ("RDMA" magic + fixed-layout
|
|
// HelloMessage), once with v3 ("RDM3" magic + protobuf RdmaHello).
|
|
// The server always accepts both via magic-byte dispatch, so this
|
|
// proves the upper-layer RPC paths behave identically under either
|
|
// wire format.
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
HandshakeVersion, RdmaRpcTest,
|
|
::testing::Values(2, 3),
|
|
[](const ::testing::TestParamInfo<int>& info) {
|
|
return std::string("v") + std::to_string(info.param);
|
|
});
|
|
|
|
#endif // if BRPC_WITH_RDMA
|
|
|
|
int main(int argc, char* argv[]) {
|
|
testing::InitGoogleTest(&argc, argv);
|
|
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
|
#if BRPC_WITH_RDMA
|
|
rdma::FLAGS_rdma_trace_verbose = true;
|
|
rdma::FLAGS_rdma_memory_pool_max_regions = 2;
|
|
FLAGS_log_idle_connection_close = true;
|
|
if (!FLAGS_rdma_test_enable) {
|
|
// skip UT requiring rdma runtime environment
|
|
rdma::g_rdma_available.store(true, butil::memory_order_relaxed);
|
|
rdma::g_skip_rdma_init = true;
|
|
}
|
|
#endif // if BRPC_WITH_RDMA
|
|
return RUN_ALL_TESTS();
|
|
}
|