Compare commits

...

2 Commits

11 changed files with 213 additions and 65 deletions

View File

@@ -324,6 +324,24 @@ jobs:
echo ""
done
test_no_uring:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_no_uring.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_interrupted_rebalance:
runs-on: ubuntu-latest
needs: build

View File

@@ -504,18 +504,21 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
int one = 1;
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
cl->peer_state = PEER_CONNECTED;
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
// EPOLLOUT is only required without io_uring, to continue partially sent messages
tfd->set_fd_handler(peer_fd, use_sync_send_recv, [this](int peer_fd, int epoll_events)
{
handle_peer_epoll(peer_fd, epoll_events);
});
// Check OSD number
init_tls_client(cl);
if (!init_tls_client(cl))
{
return;
}
check_peer_config(cl);
}
void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
{
// Mark client as ready (i.e. some data is available)
auto cl = clients_by_fd.at(peer_fd);
if (epoll_events & EPOLLRDHUP)
{
@@ -525,8 +528,9 @@ void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
fprintf(stderr, "[OSD %ju] client %ju disconnected\n", this->osd_num, cl->client_id);
}
stop_client(cl->client_id);
return;
}
else if (epoll_events & EPOLLIN)
if (epoll_events & EPOLLIN)
{
// Mark client as ready (i.e. some data is available)
cl->read_ready++;
@@ -539,6 +543,23 @@ void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
read_requests();
}
}
if (epoll_events & EPOLLOUT)
{
// The socket became writable again - continue the interrupted use_sync_send_recv
// read_requests() above may have stopped the client, so look it up again
auto cl_it = clients.find(cl->client_id);
if (cl_it == clients.end())
{
return;
}
cl = cl_it->second;
cl->write_ready = true;
// send_list is only left non-empty when we're waiting for EPOLLOUT
if (cl->send_list.size() && cl->peer_state != PEER_RDMA)
{
try_send(cl);
}
}
}
void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int errcode, uint64_t client_id)
@@ -770,9 +791,12 @@ void osd_messenger_t::accept_connections(int listen_fd)
cl->peer_fd = peer_fd;
cl->peer_state = PEER_CONNECTED;
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
init_tls_client(cl);
// Add FD to epoll
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
if (!init_tls_client(cl))
{
continue;
}
// Add FD to epoll. EPOLLOUT is only required without io_uring
tfd->set_fd_handler(peer_fd, use_sync_send_recv, [this](int peer_fd, int epoll_events)
{
handle_peer_epoll(peer_fd, epoll_events);
});

View File

@@ -147,6 +147,8 @@ struct osd_client_t
size_t write_op_pos = 0;
msghdr write_msg = { 0 };
int write_state = 0;
// Socket is writable. Only tracked with use_sync_send_recv, io_uring doesn't need it
bool write_ready = true;
std::vector<iovec> send_list;
size_t send_list_size = 0;
std::deque<osd_op_t*> send_free_ops;
@@ -248,7 +250,7 @@ protected:
void init_tls();
void destroy_tls();
void init_tls_client(osd_client_t *cl);
bool init_tls_client(osd_client_t *cl);
bool do_tls_handshake(osd_client_t *cl, bool from_recv = false);
bool derive_aes_keys(osd_client_t *cl, bool update_my, bool update_peer);
@@ -343,7 +345,8 @@ protected:
void cancel_op(osd_op_t *op);
bool try_send(osd_client_t *cl);
void handle_send(int result, bool prev, bool more, osd_client_t *cl);
bool wakeup_send(osd_client_t *cl);
bool handle_send(int result, bool prev, bool more, osd_client_t *cl);
static void post_send_free(osd_op_t *op);
bool op_write_to(osd_client_t *cl, msgr_op_writer_t & wr);
void next_write_op(osd_client_t *cl);
@@ -370,7 +373,7 @@ protected:
void op_decrypt_free(osd_client_t* cl);
#ifdef WITH_RDMA
void try_send_rdma(osd_client_t *cl);
bool try_send_rdma(osd_client_t *cl);
bool init_recv_rdma(osd_client_t *cl);
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);

View File

@@ -616,7 +616,7 @@ void osd_messenger_t::init_tls()
}
}
void osd_messenger_t::init_tls_client(osd_client_t *cl)
bool osd_messenger_t::init_tls_client(osd_client_t *cl)
{
if (gcm_enabled)
{
@@ -625,13 +625,13 @@ void osd_messenger_t::init_tls_client(osd_client_t *cl)
cl->hs->init(cl->is_incoming);
if (cl->hs->out_size())
{
if (cl->write_state == 0)
if (!wakeup_send(cl))
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
return false;
}
}
}
return true;
}
void osd_messenger_t::destroy_tls()

View File

@@ -571,7 +571,7 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
cl->rdma_conn->cur_send++;
}
void osd_messenger_t::try_send_rdma(osd_client_t *cl)
bool osd_messenger_t::try_send_rdma(osd_client_t *cl)
{
auto rc = cl->rdma_conn;
if (!rc->send_out_size)
@@ -602,7 +602,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
if (cl->io_error)
{
stop_client(cl->client_id);
return;
return false;
}
if (copied > 0)
{
@@ -622,6 +622,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
cl->send_free_ops.push_back(NULL); // end marker
}
}
return true;
}
static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
@@ -750,6 +751,7 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
}
}
} while (event_count > 0);
handle_immediate_ops();
}
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)

View File

@@ -503,7 +503,6 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev)
cl->osd_num = peer_osd;
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
cl->rdma_conn = rc;
init_tls_client(cl);
clients[conn->client_id] = cl;
if (conn->timeout_id >= 0)
{
@@ -517,6 +516,10 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev)
fprintf(stderr, "Successfully connected with OSD %ju using RDMA-CM\n", peer_osd);
}
// Add initial receive request(s)
if (!init_tls_client(cl))
{
return;
}
init_recv_rdma(cl);
if (peer_osd)
{

View File

@@ -458,8 +458,7 @@ void osd_messenger_t::read_requests()
{
result = -errno;
}
// like set_immediate
tfd->set_timer_us(0, false, [this, result, cl](int){ handle_read(result, cl); });
handle_read(result, cl);
}
}
read_ready_clients.clear();
@@ -490,8 +489,10 @@ void osd_messenger_t::handle_read(int result, osd_client_t *cl)
}
stop_client(cl->client_id);
out_wakeup:
if (set_immediate_ops.size())
if (ringloop && set_immediate_ops.size())
{
ringloop->wakeup();
}
return;
}
bool full_read = false;
@@ -501,7 +502,9 @@ out_wakeup:
{
full_read = result >= cl->read_iov.iov_len;
if (!handle_read_buffer(cl, cl->in_buf, result))
{
goto out_wakeup;
}
}
else
{
@@ -597,13 +600,16 @@ bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size
bufsize -= done;
if (cl->hs->out_size())
{
if (cl->write_state == 0)
if (!wakeup_send(cl))
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
return false;
}
}
if (cl->hs->done() && !cl->hs->out_size())
if (!cl->hs)
{
// Deleted by wakeup_send with use_sync_send_recv
}
else if (cl->hs->done() && !cl->hs->out_size())
{
// Delete hs when done and nothing to send
delete cl->hs;

View File

@@ -17,6 +17,10 @@
#define GCM_TMP_BUF_SIZE 4096
// How many times to retry sendmsg() in place before falling back to EPOLLOUT.
// Only used with use_sync_send_recv - io_uring waits for the socket by itself
#define MSGR_SEND_RETRIES 4
class msgr_op_writer_t
{
public:
@@ -467,36 +471,60 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
return;
}
#endif
if (!ringloop)
try_send(cl);
}
bool osd_messenger_t::wakeup_send(osd_client_t *cl)
{
if (cl->peer_state == PEER_RDMA)
{
// FIXME: It's worse because it doesn't allow batching
while (cl->write_op || cl->write_ops.size())
{
try_send(cl);
}
return try_send_rdma(cl);
}
else
if (use_sync_send_recv)
{
if (!try_send(cl) && cl->write_state == 0)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
ringloop->wakeup();
// FIXME: Batch send/recv without io_uring too.
return try_send(cl);
}
if (cl->write_state == 0)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
ringloop->wakeup();
return true;
}
bool osd_messenger_t::try_send(osd_client_t *cl)
{
assert(cl->peer_state != PEER_RDMA);
restart:
if (cl->peer_state == PEER_STOPPED || cl->peer_fd < 0)
{
return true;
}
if (cl->write_msg.msg_iovlen > 0 || !ringloop->space_left() && !use_sync_send_recv)
if (cl->write_msg.msg_iovlen > 0)
{
return false;
return true;
}
if (!use_sync_send_recv)
{
if (!ringloop->space_left())
{
if (cl->write_state == 0)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
ringloop->wakeup();
return true;
}
}
else if (!cl->write_ready)
{
// Socket buffer is full, the unsent tail is still in send_list.
// Do not copy more data into it, just wait for EPOLLOUT
return true;
}
assert(cl->peer_state != PEER_RDMA);
if (cl->hs)
{
// Send handshake message
@@ -523,11 +551,10 @@ copy_ops:
if (cl->io_error)
{
stop_client(cl->client_id);
return true;
return false;
}
if (!cl->send_list.size())
{
cl->write_state = 0;
return true;
}
// get_op_writer_t already guarantees that it's under IOV_MAX
@@ -575,16 +602,29 @@ copy_ops:
}
else
{
cl->write_msg.msg_iov = cl->send_list.data();
cl->write_msg.msg_iovlen = cl->send_list.size();
cl->refs++;
int result = sendmsg(cl->peer_fd, &cl->write_msg, MSG_NOSIGNAL);
if (result < 0)
// Try to first send with a busy loop, then postpone to the EPOLLOUT handler
int send_retries = MSGR_SEND_RETRIES;
while (cl->send_list.size())
{
result = -errno;
cl->write_msg.msg_iov = cl->send_list.data();
cl->write_msg.msg_iovlen = cl->send_list.size();
cl->refs++;
int result = sendmsg(cl->peer_fd, &cl->write_msg, MSG_NOSIGNAL);
if (result < 0)
{
result = -errno;
}
if (!handle_send(result, false, false, cl))
{
return false;
}
if (cl->send_list.size() && --send_retries <= 0)
{
cl->write_ready = false;
return true;
}
}
// like set_immediate
tfd->set_timer_us(0, false, [this, result, cl](int){ handle_send(result, false, false, cl); });
goto restart;
}
return true;
}
@@ -664,21 +704,42 @@ void osd_messenger_t::send_replies()
{
uint64_t client_id = write_ready_clients[i];
auto cl_it = clients.find(client_id);
if (cl_it != clients.end() && cl_it->second->peer_state != PEER_RDMA && !try_send(cl_it->second))
if (cl_it != clients.end() && cl_it->second->peer_state != PEER_RDMA)
{
write_ready_clients.erase(write_ready_clients.begin(), write_ready_clients.begin() + i);
return;
if (!ringloop->space_left())
{
write_ready_clients.erase(write_ready_clients.begin(), write_ready_clients.begin() + i);
return;
}
cl_it->second->write_state = 0;
try_send(cl_it->second);
}
}
write_ready_clients.clear();
}
void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t *cl)
static void eat_send_list(osd_client_t *cl, size_t n)
{
int i = 0;
while (i < cl->send_list.size() && n >= cl->send_list[i].iov_len)
{
n -= cl->send_list[i].iov_len;
i++;
}
if (i > 0)
cl->send_list.erase(cl->send_list.begin(), cl->send_list.begin()+i);
if (cl->send_list.size())
{
cl->send_list[0].iov_base = (uint8_t*)cl->send_list[0].iov_base + n;
cl->send_list[0].iov_len -= n;
}
}
bool osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t *cl)
{
if (!prev)
{
cl->write_msg.msg_iovlen = 0;
cl->send_list.clear();
}
if (!more)
{
@@ -690,14 +751,15 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
{
destroy_client(cl);
}
return;
// FIXME: Return error codes instead of true/false (?)
return false;
}
if (result < 0 && result != -EAGAIN && result != -EINTR)
{
// this is a client socket, so don't panic. just disconnect it
fprintf(stderr, "Client %ju socket write error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result));
stop_client(cl->client_id);
return;
return false;
}
if (result >= 0)
{
@@ -709,14 +771,26 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
post_send_free(cl->zc_free_list[i]);
if (i > 0)
cl->zc_free_list.erase(cl->zc_free_list.begin(), cl->zc_free_list.begin()+i+1);
return;
return true;
}
if (cl->send_list_size > result)
if (cl->send_list_size <= result)
{
cl->send_list.clear();
cl->send_list_size = 0;
}
else if (!use_sync_send_recv)
{
fprintf(stderr, "Client %ju socket write error: expected to send "
"%zu bytes with MSG_WAITALL but sent %u. Disconnecting client\n", cl->client_id, cl->send_list_size, result);
stop_client(cl->client_id);
return;
return false;
}
else
{
// Partially sent
eat_send_list(cl, result);
cl->send_list_size -= result;
return true;
}
for (auto op: cl->send_free_ops)
{
@@ -728,9 +802,6 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
if (more)
cl->zc_free_list.push_back(NULL); // end marker
cl->send_free_ops.clear();
cl->write_state = 0;
if (cl->write_op || cl->write_ops.size())
cl->write_state = CL_WRITE_READY;
if ((cl->proto_csum_status & MSGR_CSUM_NEG) && !cl->write_op && !cl->write_ops.size())
{
// Checksums negotiated, enable
@@ -750,10 +821,16 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
}
#endif
}
if (cl->write_state != 0)
if ((cl->write_op || cl->write_ops.size() || cl->hs && cl->hs->out_size()) && !use_sync_send_recv)
{
write_ready_clients.push_back(cl->client_id);
if (cl->write_state == 0)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
ringloop->wakeup();
}
return true;
}
static inline bool op_has_data_for_ssl(osd_op_t *op)

View File

@@ -73,6 +73,11 @@ void osd_messenger_t::accept_connections(int listen_fd)
{
}
bool osd_messenger_t::wakeup_send(osd_client_t *cl)
{
return true;
}
#ifdef WITH_RDMA
json11::Json osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address, uint64_t client_max_msg)
{

View File

@@ -27,6 +27,8 @@ ETCD_SCHEME=https ./test_snapshot.sh
./test_img_online_resize.sh
./test_no_uring.sh
./test_interrupted_rebalance.sh
IMMEDIATE_COMMIT=1 ./test_interrupted_rebalance.sh
SCHEME=ec ./test_interrupted_rebalance.sh

8
tests/test_no_uring.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash -ex
. `dirname $0`/run_3osds.sh
$VITASTOR_FIO -bs=128k -direct=1 -numjobs=1 -iodepth=4 \
-rw=write -pool=1 -inode=1 -size=128M -runtime=10 -no_io_uring=1
format_green OK