Программа RDMA случайно зависает
Кто-нибудь, кто занимался программированием RDMA с использованием библиотеки RDMA_CM?
Мне трудно найти даже простые примеры для изучения. В librdmacm есть пример rdma_client & rdma_server, но он не запускается в цикле (rping делает цикл, но он написан с использованием глаголов IB напрямую вместо функций rdma_cm).
Я собрал тривиальную программу для пинг-понга, но она зависает в любом месте после 1 - 100 отскоков. Я обнаружил, что добавление спящего режима внутри клиента заставляет его работать дольше перед зависанием, что указывает на состояние гонки.
Клиент застревает в rdma_get_send_comp (), а сервер застревает в rdma_get_recv_comp ().
Мое ограниченное понимание заключается в том, что перед каждым вызовом rdma_post_send() вам необходимо выполнить rdma_post_recv (), который будет приходить после отправки. Также перед каждой отправкой (за исключением 1-й клиентской отправки) вам нужно дождаться сообщения (rdma_get_recv()), указывающего, что другая сторона готова к приему.
Что может быть не так?
Server(rdma_cm_id *id)
{
ibv_wc wc;
int ret;
uint8_t recvBuffer[MESSAGE_BUFFER_SIZE],
sendBuffer[MESSAGE_BUFFER_SIZE];
ibv_mr *recvMemRegion = rdma_reg_msgs(id, recvBuffer, MESSAGE_BUFFER_SIZE);
if (!recvMemRegion)
throw 0;
ibv_mr *sendMemRegion = rdma_reg_msgs(id, sendBuffer, MESSAGE_BUFFER_SIZE);
if (!sendMemRegion)
throw 0;
if (ret = rdma_post_recv(id, NULL, recvBuffer, 1, recvMemRegion))
throw 0;
if (ret = rdma_accept(id, NULL))
throw 0;
do
{
if ((ret = rdma_get_recv_comp(id, &wc)) <= 0)
throw 0;
if (ret = rdma_post_recv(id, NULL, recvBuffer, 1, recvMemRegion))
throw 0;
if (ret = rdma_post_send(id, NULL, sendBuffer, 1, sendMemRegion, 0))
throw 0;
printf(".");
fflush(stdout);
if ((ret = rdma_get_send_comp(id, &wc)) <= 0)
throw 0;
}
while (true);
}
Client() // sends the 1st message
{
// queue-pair parameters are:
attr.cap.max_send_wr = attr.cap.max_recv_wr = 4;
attr.cap.max_send_sge = attr.cap.max_recv_sge = 2;
attr.cap.max_inline_data = 16;
attr.qp_context = id;
attr.sq_sig_all = 1;
attr.qp_type = IBV_QPT_RC;
<create connection boiler plate>
uint8_t recvBuffer[MESSAGE_BUFFER_SIZE],
sendBuffer[MESSAGE_BUFFER_SIZE];
recvMemRegion = rdma_reg_msgs(id, recvBuffer, MESSAGE_BUFFER_SIZE);
if (!recvMemRegion)
throw 0;
sendMemRegion = rdma_reg_msgs(id, sendBuffer, MESSAGE_BUFFER_SIZE);
if (!sendMemRegion)
throw 0;
if (ret = rdma_connect(id, NULL))
throw 0;
do
{
if (ret = rdma_post_recv(id, NULL, recvBuffer, 1, recvMemRegion))
throw 0;
//usleep(5000);
if (ret = rdma_post_send(id, NULL, sendBuffer, 1, sendMemRegion, 0))
throw 0;
if ((ret = rdma_get_send_comp(id, &wc)) <= 0)
throw 0;
if ((ret = rdma_get_recv_comp(id, &wc)) <= 0)
throw 0;
printf(".");
fflush(stdout);
}
while (true);
}
1 ответ
Проклятия! Меня поразила ошибка в librdmacm-1.0.15-1 (с 2012 года), которая пришла с SUSE 11. Я знал, что в моей последовательности send/recv нет ничего плохого.
Сначала я попытался сравнить мой код с другими примерами. В одном примере я видел
while (!ibv_poll_cq(id->send_cq, 1, &wc));
вместо rdma_get_send_comp() и аналогично для rdma_get_recv_comp(). Я попытался заменить их в моем примере, и чудесным образом зависание пропало!
Хм, может быть, rdma_get_send_comp() не делает то, что я ожидаю. Я лучше посмотрю на код. Я получил код для 1.0.15 и 1.0.18 и что я вижу в rdma_verbs.h?
2 очень разных последовательности глаголов IB:
// 1.0.15
rdma_get_send_comp(struct rdma_cm_id *id, struct ibv_wc *wc)
{
struct ibv_cq *cq;
void *context;
int ret;
ret = ibv_poll_cq(id->send_cq, 1, wc);
if (ret)
goto out;
ret = ibv_req_notify_cq(id->send_cq, 0);
if (ret)
return rdma_seterrno(ret);
while (!(ret = ibv_poll_cq(id->send_cq, 1, wc))) {
ret = ibv_get_cq_event(id->send_cq_channel, &cq, &context);
if (ret)
return rdma_seterrno(ret);
assert(cq == id->send_cq && context == id);
ibv_ack_cq_events(id->send_cq, 1);
}
out:
return (ret < 0) ? rdma_seterrno(ret) : ret;
}
vs
// 1.0.18
rdma_get_send_comp(struct rdma_cm_id *id, struct ibv_wc *wc)
{
struct ibv_cq *cq;
void *context;
int ret;
do {
ret = ibv_poll_cq(id->send_cq, 1, wc);
if (ret)
break;
ret = ibv_req_notify_cq(id->send_cq, 0);
if (ret)
return rdma_seterrno(ret);
ret = ibv_poll_cq(id->send_cq, 1, wc);
if (ret)
break;
ret = ibv_get_cq_event(id->send_cq_channel, &cq, &context);
if (ret)
return ret;
assert(cq == id->send_cq && context == id);
ibv_ack_cq_events(id->send_cq, 1);
} while (1);
return (ret < 0) ? rdma_seterrno(ret) : ret;
}
Кто-нибудь может объяснить, почему 1.0.18 работает, а 1.0.15 случайно зависает?