forked from vitalif/vitastor
Support RDMA without ODP by stupidly copying memory. Disable ODP by default
ODP is slower than regular RDMA even with memory copy overhead Example numbers: - 3950000 random read iops without ODP vs 240000 iops with ODP - 1447000 random write iops without ODP vs 101000 iops with ODP Reference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
parent
7e82573ed0
commit
225eb2fe3d
|
@ -20,6 +20,7 @@ between clients, OSDs and etcd.
|
||||||
- [rdma_max_msg](#rdma_max_msg)
|
- [rdma_max_msg](#rdma_max_msg)
|
||||||
- [rdma_max_recv](#rdma_max_recv)
|
- [rdma_max_recv](#rdma_max_recv)
|
||||||
- [rdma_max_send](#rdma_max_send)
|
- [rdma_max_send](#rdma_max_send)
|
||||||
|
- [rdma_odp](#rdma_odp)
|
||||||
- [peer_connect_interval](#peer_connect_interval)
|
- [peer_connect_interval](#peer_connect_interval)
|
||||||
- [peer_connect_timeout](#peer_connect_timeout)
|
- [peer_connect_timeout](#peer_connect_timeout)
|
||||||
- [osd_idle_timeout](#osd_idle_timeout)
|
- [osd_idle_timeout](#osd_idle_timeout)
|
||||||
|
@ -68,11 +69,14 @@ but they are not connected to the cluster.
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
RDMA device name to use for Vitastor OSD communications (for example,
|
RDMA device name to use for Vitastor OSD communications (for example,
|
||||||
"rocep5s0f0"). Please note that Vitastor RDMA requires Implicit On-Demand
|
"rocep5s0f0"). Now Vitastor supports all adapters, even ones without
|
||||||
Paging (Implicit ODP) and Scatter/Gather (SG) support from the RDMA device
|
ODP support, like Mellanox ConnectX-3 and non-Mellanox cards.
|
||||||
to work. For example, Mellanox ConnectX-3 and older adapters don't have
|
|
||||||
Implicit ODP, so they're unsupported by Vitastor. Run `ibv_devinfo -v` as
|
Versions up to Vitastor 1.2.0 required ODP which is only present in
|
||||||
root to list available RDMA devices and their features.
|
Mellanox ConnectX >= 4. See also [rdma_odp](#rdma_odp).
|
||||||
|
|
||||||
|
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
||||||
|
features.
|
||||||
|
|
||||||
Remember that you also have to configure your network switches if you use
|
Remember that you also have to configure your network switches if you use
|
||||||
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
||||||
|
@ -147,6 +151,28 @@ less than `rdma_max_recv` so the receiving side doesn't run out of buffers.
|
||||||
Doesn't affect memory usage - additional memory isn't allocated for send
|
Doesn't affect memory usage - additional memory isn't allocated for send
|
||||||
operations.
|
operations.
|
||||||
|
|
||||||
|
## rdma_odp
|
||||||
|
|
||||||
|
- Type: boolean
|
||||||
|
- Default: false
|
||||||
|
|
||||||
|
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
|
||||||
|
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
|
||||||
|
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
|
||||||
|
copying during sending. One would think this should improve performance, but
|
||||||
|
**in reality** RDMA performance with ODP is **drastically** worse. Example
|
||||||
|
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
|
||||||
|
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
|
||||||
|
|
||||||
|
This happens because Mellanox ODP implementation seems to be based on
|
||||||
|
message retransmissions when the adapter doesn't know about the buffer yet -
|
||||||
|
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
||||||
|
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
||||||
|
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
|
|
||||||
|
ODP support is retained in the code just in case a good ODP implementation
|
||||||
|
appears one day.
|
||||||
|
|
||||||
## peer_connect_interval
|
## peer_connect_interval
|
||||||
|
|
||||||
- Type: seconds
|
- Type: seconds
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
- [rdma_max_msg](#rdma_max_msg)
|
- [rdma_max_msg](#rdma_max_msg)
|
||||||
- [rdma_max_recv](#rdma_max_recv)
|
- [rdma_max_recv](#rdma_max_recv)
|
||||||
- [rdma_max_send](#rdma_max_send)
|
- [rdma_max_send](#rdma_max_send)
|
||||||
|
- [rdma_odp](#rdma_odp)
|
||||||
- [peer_connect_interval](#peer_connect_interval)
|
- [peer_connect_interval](#peer_connect_interval)
|
||||||
- [peer_connect_timeout](#peer_connect_timeout)
|
- [peer_connect_timeout](#peer_connect_timeout)
|
||||||
- [osd_idle_timeout](#osd_idle_timeout)
|
- [osd_idle_timeout](#osd_idle_timeout)
|
||||||
|
@ -71,12 +72,15 @@ RDMA может быть нужно только если у клиентов е
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
||||||
Имейте в виду, что поддержка RDMA в Vitastor требует функций устройства
|
Сейчас Vitastor поддерживает все модели адаптеров, включая те, у которых
|
||||||
Implicit On-Demand Paging (Implicit ODP) и Scatter/Gather (SG). Например,
|
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
|
||||||
адаптеры Mellanox ConnectX-3 и более старые не поддерживают Implicit ODP и
|
картами производства не Mellanox.
|
||||||
потому не поддерживаются в Vitastor. Запустите `ibv_devinfo -v` от имени
|
|
||||||
суперпользователя, чтобы посмотреть список доступных RDMA-устройств, их
|
Версии Vitastor до 1.2.0 включительно требовали ODP, который есть только
|
||||||
параметры и возможности.
|
на Mellanox ConnectX 4 и более новых. См. также [rdma_odp](#rdma_odp).
|
||||||
|
|
||||||
|
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
||||||
|
список доступных RDMA-устройств, их параметры и возможности.
|
||||||
|
|
||||||
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
||||||
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
||||||
|
@ -155,6 +159,29 @@ OSD в любом случае согласовывают реальное зн
|
||||||
Не влияет на потребление памяти - дополнительная память на операции отправки
|
Не влияет на потребление памяти - дополнительная память на операции отправки
|
||||||
не выделяется.
|
не выделяется.
|
||||||
|
|
||||||
|
## rdma_odp
|
||||||
|
|
||||||
|
- Тип: булево (да/нет)
|
||||||
|
- Значение по умолчанию: false
|
||||||
|
|
||||||
|
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
|
||||||
|
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
|
||||||
|
не регистрировать память для её использования RDMA-картой. Благодаря этому
|
||||||
|
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
|
||||||
|
улучшать производительность - но **по факту** получается так, что
|
||||||
|
производительность только ухудшается, причём сильно. Пример - на 3-узловом
|
||||||
|
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
|
||||||
|
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
|
||||||
|
|
||||||
|
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
||||||
|
основана на повторной передаче сообщений, когда карте не известен буфер -
|
||||||
|
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
||||||
|
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
||||||
|
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
|
|
||||||
|
Возможность использования ODP сохранена в коде на случай, если вдруг в один
|
||||||
|
прекрасный день появится хорошая реализация ODP.
|
||||||
|
|
||||||
## peer_connect_interval
|
## peer_connect_interval
|
||||||
|
|
||||||
- Тип: секунды
|
- Тип: секунды
|
||||||
|
|
|
@ -48,11 +48,14 @@
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
RDMA device name to use for Vitastor OSD communications (for example,
|
RDMA device name to use for Vitastor OSD communications (for example,
|
||||||
"rocep5s0f0"). Please note that Vitastor RDMA requires Implicit On-Demand
|
"rocep5s0f0"). Now Vitastor supports all adapters, even ones without
|
||||||
Paging (Implicit ODP) and Scatter/Gather (SG) support from the RDMA device
|
ODP support, like Mellanox ConnectX-3 and non-Mellanox cards.
|
||||||
to work. For example, Mellanox ConnectX-3 and older adapters don't have
|
|
||||||
Implicit ODP, so they're unsupported by Vitastor. Run `ibv_devinfo -v` as
|
Versions up to Vitastor 1.2.0 required ODP which is only present in
|
||||||
root to list available RDMA devices and their features.
|
Mellanox ConnectX >= 4. See also [rdma_odp](#rdma_odp).
|
||||||
|
|
||||||
|
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
||||||
|
features.
|
||||||
|
|
||||||
Remember that you also have to configure your network switches if you use
|
Remember that you also have to configure your network switches if you use
|
||||||
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
||||||
|
@ -61,12 +64,15 @@
|
||||||
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
||||||
Имейте в виду, что поддержка RDMA в Vitastor требует функций устройства
|
Сейчас Vitastor поддерживает все модели адаптеров, включая те, у которых
|
||||||
Implicit On-Demand Paging (Implicit ODP) и Scatter/Gather (SG). Например,
|
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
|
||||||
адаптеры Mellanox ConnectX-3 и более старые не поддерживают Implicit ODP и
|
картами производства не Mellanox.
|
||||||
потому не поддерживаются в Vitastor. Запустите `ibv_devinfo -v` от имени
|
|
||||||
суперпользователя, чтобы посмотреть список доступных RDMA-устройств, их
|
Версии Vitastor до 1.2.0 включительно требовали ODP, который есть только
|
||||||
параметры и возможности.
|
на Mellanox ConnectX 4 и более новых. См. также [rdma_odp](#rdma_odp).
|
||||||
|
|
||||||
|
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
||||||
|
список доступных RDMA-устройств, их параметры и возможности.
|
||||||
|
|
||||||
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
||||||
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
||||||
|
@ -160,6 +166,45 @@
|
||||||
у принимающей стороны в процессе работы не заканчивались буферы на приём.
|
у принимающей стороны в процессе работы не заканчивались буферы на приём.
|
||||||
Не влияет на потребление памяти - дополнительная память на операции отправки
|
Не влияет на потребление памяти - дополнительная память на операции отправки
|
||||||
не выделяется.
|
не выделяется.
|
||||||
|
- name: rdma_odp
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
online: false
|
||||||
|
info: |
|
||||||
|
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
|
||||||
|
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
|
||||||
|
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
|
||||||
|
copying during sending. One would think this should improve performance, but
|
||||||
|
**in reality** RDMA performance with ODP is **drastically** worse. Example
|
||||||
|
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
|
||||||
|
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
|
||||||
|
|
||||||
|
This happens because Mellanox ODP implementation seems to be based on
|
||||||
|
message retransmissions when the adapter doesn't know about the buffer yet -
|
||||||
|
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
||||||
|
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
||||||
|
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
|
|
||||||
|
ODP support is retained in the code just in case a good ODP implementation
|
||||||
|
appears one day.
|
||||||
|
info_ru: |
|
||||||
|
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
|
||||||
|
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
|
||||||
|
не регистрировать память для её использования RDMA-картой. Благодаря этому
|
||||||
|
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
|
||||||
|
улучшать производительность - но **по факту** получается так, что
|
||||||
|
производительность только ухудшается, причём сильно. Пример - на 3-узловом
|
||||||
|
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
|
||||||
|
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
|
||||||
|
|
||||||
|
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
||||||
|
основана на повторной передаче сообщений, когда карте не известен буфер -
|
||||||
|
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
||||||
|
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
||||||
|
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
|
|
||||||
|
Возможность использования ODP сохранена в коде на случай, если вдруг в один
|
||||||
|
прекрасный день появится хорошая реализация ODP.
|
||||||
- name: peer_connect_interval
|
- name: peer_connect_interval
|
||||||
type: sec
|
type: sec
|
||||||
min: 1
|
min: 1
|
||||||
|
|
|
@ -22,7 +22,7 @@ void osd_messenger_t::init()
|
||||||
{
|
{
|
||||||
rdma_context = msgr_rdma_context_t::create(
|
rdma_context = msgr_rdma_context_t::create(
|
||||||
rdma_device != "" ? rdma_device.c_str() : NULL,
|
rdma_device != "" ? rdma_device.c_str() : NULL,
|
||||||
rdma_port_num, rdma_gid_index, rdma_mtu, log_level
|
rdma_port_num, rdma_gid_index, rdma_mtu, rdma_odp, log_level
|
||||||
);
|
);
|
||||||
if (!rdma_context)
|
if (!rdma_context)
|
||||||
{
|
{
|
||||||
|
@ -167,6 +167,7 @@ void osd_messenger_t::parse_config(const json11::Json & config)
|
||||||
this->rdma_max_msg = config["rdma_max_msg"].uint64_value();
|
this->rdma_max_msg = config["rdma_max_msg"].uint64_value();
|
||||||
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
|
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
|
||||||
this->rdma_max_msg = 129*1024;
|
this->rdma_max_msg = 129*1024;
|
||||||
|
this->rdma_odp = config["rdma_odp"].bool_value();
|
||||||
#endif
|
#endif
|
||||||
this->receive_buffer_size = (uint32_t)config["tcp_header_buffer_size"].uint64_value();
|
this->receive_buffer_size = (uint32_t)config["tcp_header_buffer_size"].uint64_value();
|
||||||
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
|
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
|
||||||
|
|
|
@ -131,6 +131,7 @@ protected:
|
||||||
msgr_rdma_context_t *rdma_context = NULL;
|
msgr_rdma_context_t *rdma_context = NULL;
|
||||||
uint64_t rdma_max_sge = 0, rdma_max_send = 0, rdma_max_recv = 0;
|
uint64_t rdma_max_sge = 0, rdma_max_send = 0, rdma_max_recv = 0;
|
||||||
uint64_t rdma_max_msg = 0;
|
uint64_t rdma_max_msg = 0;
|
||||||
|
bool rdma_odp = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<int> read_ready_clients;
|
std::vector<int> read_ready_clients;
|
||||||
|
@ -197,7 +198,9 @@ protected:
|
||||||
void handle_reply_ready(osd_op_t *op);
|
void handle_reply_ready(osd_op_t *op);
|
||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
bool try_send_rdma(osd_client_t *cl);
|
void try_send_rdma(osd_client_t *cl);
|
||||||
|
void try_send_rdma_odp(osd_client_t *cl);
|
||||||
|
void try_send_rdma_nodp(osd_client_t *cl);
|
||||||
bool try_recv_rdma(osd_client_t *cl);
|
bool try_recv_rdma(osd_client_t *cl);
|
||||||
void handle_rdma_events();
|
void handle_rdma_events();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -47,11 +47,29 @@ msgr_rdma_connection_t::~msgr_rdma_connection_t()
|
||||||
if (qp)
|
if (qp)
|
||||||
ibv_destroy_qp(qp);
|
ibv_destroy_qp(qp);
|
||||||
if (recv_buffers.size())
|
if (recv_buffers.size())
|
||||||
|
{
|
||||||
for (auto b: recv_buffers)
|
for (auto b: recv_buffers)
|
||||||
free(b);
|
{
|
||||||
|
if (b.mr)
|
||||||
|
ibv_dereg_mr(b.mr);
|
||||||
|
free(b.buf);
|
||||||
|
}
|
||||||
|
recv_buffers.clear();
|
||||||
|
}
|
||||||
|
if (send_out.mr)
|
||||||
|
{
|
||||||
|
ibv_dereg_mr(send_out.mr);
|
||||||
|
send_out.mr = NULL;
|
||||||
|
}
|
||||||
|
if (send_out.buf)
|
||||||
|
{
|
||||||
|
free(send_out.buf);
|
||||||
|
send_out.buf = NULL;
|
||||||
|
}
|
||||||
|
send_out_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
msgr_rdma_context_t *msgr_rdma_context_t::create(const char *ib_devname, uint8_t ib_port, uint8_t gid_index, uint32_t mtu, int log_level)
|
msgr_rdma_context_t *msgr_rdma_context_t::create(const char *ib_devname, uint8_t ib_port, uint8_t gid_index, uint32_t mtu, bool odp, int log_level)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
ibv_device **dev_list = NULL;
|
ibv_device **dev_list = NULL;
|
||||||
|
@ -136,22 +154,28 @@ msgr_rdma_context_t *msgr_rdma_context_t::create(const char *ib_devname, uint8_t
|
||||||
fprintf(stderr, "Couldn't query RDMA device for its features\n");
|
fprintf(stderr, "Couldn't query RDMA device for its features\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
if (!(ctx->attrx.odp_caps.general_caps & IBV_ODP_SUPPORT) ||
|
ctx->odp = odp;
|
||||||
|
if (ctx->odp &&
|
||||||
|
(!(ctx->attrx.odp_caps.general_caps & IBV_ODP_SUPPORT) ||
|
||||||
!(ctx->attrx.odp_caps.general_caps & IBV_ODP_SUPPORT_IMPLICIT) ||
|
!(ctx->attrx.odp_caps.general_caps & IBV_ODP_SUPPORT_IMPLICIT) ||
|
||||||
!(ctx->attrx.odp_caps.per_transport_caps.rc_odp_caps & IBV_ODP_SUPPORT_SEND) ||
|
!(ctx->attrx.odp_caps.per_transport_caps.rc_odp_caps & IBV_ODP_SUPPORT_SEND) ||
|
||||||
!(ctx->attrx.odp_caps.per_transport_caps.rc_odp_caps & IBV_ODP_SUPPORT_RECV))
|
!(ctx->attrx.odp_caps.per_transport_caps.rc_odp_caps & IBV_ODP_SUPPORT_RECV)))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "The RDMA device isn't implicit ODP (On-Demand Paging) capable or does not support RC send and receive with ODP\n");
|
ctx->odp = false;
|
||||||
goto cleanup;
|
if (log_level > 0)
|
||||||
|
fprintf(stderr, "The RDMA device isn't implicit ODP (On-Demand Paging) capable, disabling it\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->odp)
|
||||||
|
{
|
||||||
ctx->mr = ibv_reg_mr(ctx->pd, NULL, SIZE_MAX, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_ON_DEMAND);
|
ctx->mr = ibv_reg_mr(ctx->pd, NULL, SIZE_MAX, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_ON_DEMAND);
|
||||||
if (!ctx->mr)
|
if (!ctx->mr)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Couldn't register RDMA memory region\n");
|
fprintf(stderr, "Couldn't register RDMA memory region\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx->channel = ibv_create_comp_channel(ctx->context);
|
ctx->channel = ibv_create_comp_channel(ctx->context);
|
||||||
if (!ctx->channel)
|
if (!ctx->channel)
|
||||||
|
@ -365,12 +389,34 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
|
||||||
cl->rdma_conn->cur_send++;
|
cl->rdma_conn->cur_send++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
static int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
|
||||||
|
{
|
||||||
|
auto rc = cl->rdma_conn;
|
||||||
|
int total_dst_len = dst_len;
|
||||||
|
while (dst_len > 0 && rc->send_pos < cl->send_list.size())
|
||||||
|
{
|
||||||
|
iovec & iov = cl->send_list[rc->send_pos];
|
||||||
|
uint32_t len = (uint32_t)(iov.iov_len-rc->send_buf_pos < dst_len
|
||||||
|
? iov.iov_len-rc->send_buf_pos : dst_len);
|
||||||
|
memcpy(dst, iov.iov_base+rc->send_buf_pos, len);
|
||||||
|
dst += len;
|
||||||
|
dst_len -= len;
|
||||||
|
rc->send_buf_pos += len;
|
||||||
|
if (rc->send_buf_pos >= iov.iov_len)
|
||||||
|
{
|
||||||
|
rc->send_pos++;
|
||||||
|
rc->send_buf_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total_dst_len-dst_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::try_send_rdma_odp(osd_client_t *cl)
|
||||||
{
|
{
|
||||||
auto rc = cl->rdma_conn;
|
auto rc = cl->rdma_conn;
|
||||||
if (!cl->send_list.size() || rc->cur_send >= rc->max_send)
|
if (!cl->send_list.size() || rc->cur_send >= rc->max_send)
|
||||||
{
|
{
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
uint64_t op_size = 0, op_sge = 0;
|
uint64_t op_size = 0, op_sge = 0;
|
||||||
ibv_sge sge[rc->max_sge];
|
ibv_sge sge[rc->max_sge];
|
||||||
|
@ -408,15 +454,70 @@ bool osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||||
rc->send_sizes.push_back(op_size);
|
rc->send_sizes.push_back(op_size);
|
||||||
try_send_rdma_wr(cl, sge, op_sge);
|
try_send_rdma_wr(cl, sge, op_sge);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
|
void osd_messenger_t::try_send_rdma_nodp(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
auto rc = cl->rdma_conn;
|
||||||
|
if (!rc->send_out_size)
|
||||||
|
{
|
||||||
|
// Allocate send ring buffer, if not yet
|
||||||
|
rc->send_out_size = rc->max_msg*rdma_max_send;
|
||||||
|
rc->send_out.buf = malloc_or_die(rc->send_out_size);
|
||||||
|
if (!rdma_context->odp)
|
||||||
|
{
|
||||||
|
rc->send_out.mr = ibv_reg_mr(rdma_context->pd, rc->send_out.buf, rc->send_out_size, 0);
|
||||||
|
if (!rc->send_out.mr)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to register RDMA memory region: %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy data into the buffer and send it
|
||||||
|
uint8_t *dst = NULL;
|
||||||
|
int dst_len = 0;
|
||||||
|
int copied = 1;
|
||||||
|
while (!rc->send_out_full && copied > 0 && rc->cur_send < rc->max_send)
|
||||||
|
{
|
||||||
|
dst = (uint8_t*)rc->send_out.buf + rc->send_out_pos;
|
||||||
|
dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
|
||||||
|
if (dst_len > rc->max_msg)
|
||||||
|
dst_len = rc->max_msg;
|
||||||
|
copied = try_send_rdma_copy(cl, dst, dst_len);
|
||||||
|
if (copied > 0)
|
||||||
|
{
|
||||||
|
rc->send_out_pos += copied;
|
||||||
|
if (rc->send_out_pos == rc->send_out_size)
|
||||||
|
rc->send_out_pos = 0;
|
||||||
|
assert(rc->send_out_pos < rc->send_out_size);
|
||||||
|
if (rc->send_out_pos >= rc->send_done_pos)
|
||||||
|
rc->send_out_full = true;
|
||||||
|
ibv_sge sge = {
|
||||||
|
.addr = (uintptr_t)dst,
|
||||||
|
.length = (uint32_t)copied,
|
||||||
|
.lkey = rdma_context->odp ? rdma_context->mr->lkey : rc->send_out.mr->lkey,
|
||||||
|
};
|
||||||
|
try_send_rdma_wr(cl, &sge, 1);
|
||||||
|
rc->send_sizes.push_back(copied);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
if (rdma_context->odp)
|
||||||
|
try_send_rdma_odp(cl);
|
||||||
|
else
|
||||||
|
try_send_rdma_nodp(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void try_recv_rdma_wr(osd_client_t *cl, msgr_rdma_buf_t b)
|
||||||
{
|
{
|
||||||
ibv_sge sge = {
|
ibv_sge sge = {
|
||||||
.addr = (uintptr_t)buf,
|
.addr = (uintptr_t)b.buf,
|
||||||
.length = (uint32_t)cl->rdma_conn->max_msg,
|
.length = (uint32_t)cl->rdma_conn->max_msg,
|
||||||
.lkey = cl->rdma_conn->ctx->mr->lkey,
|
.lkey = cl->rdma_conn->ctx->odp ? cl->rdma_conn->ctx->mr->lkey : b.mr->lkey,
|
||||||
};
|
};
|
||||||
ibv_recv_wr *bad_wr = NULL;
|
ibv_recv_wr *bad_wr = NULL;
|
||||||
ibv_recv_wr wr = {
|
ibv_recv_wr wr = {
|
||||||
|
@ -438,9 +539,19 @@ bool osd_messenger_t::try_recv_rdma(osd_client_t *cl)
|
||||||
auto rc = cl->rdma_conn;
|
auto rc = cl->rdma_conn;
|
||||||
while (rc->cur_recv < rc->max_recv)
|
while (rc->cur_recv < rc->max_recv)
|
||||||
{
|
{
|
||||||
void *buf = malloc_or_die(rc->max_msg);
|
msgr_rdma_buf_t b;
|
||||||
rc->recv_buffers.push_back(buf);
|
b.buf = malloc_or_die(rc->max_msg);
|
||||||
try_recv_rdma_wr(cl, buf);
|
if (!rdma_context->odp)
|
||||||
|
{
|
||||||
|
b.mr = ibv_reg_mr(rdma_context->pd, b.buf, rc->max_msg, IBV_ACCESS_LOCAL_WRITE);
|
||||||
|
if (!b.mr)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to register RDMA memory region: %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc->recv_buffers.push_back(b);
|
||||||
|
try_recv_rdma_wr(cl, b);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -492,7 +603,7 @@ void osd_messenger_t::handle_rdma_events()
|
||||||
if (!is_send)
|
if (!is_send)
|
||||||
{
|
{
|
||||||
rc->cur_recv--;
|
rc->cur_recv--;
|
||||||
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
|
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf].buf, wc[i].byte_len))
|
||||||
{
|
{
|
||||||
// handle_read_buffer may stop the client
|
// handle_read_buffer may stop the client
|
||||||
continue;
|
continue;
|
||||||
|
@ -505,6 +616,14 @@ void osd_messenger_t::handle_rdma_events()
|
||||||
rc->cur_send--;
|
rc->cur_send--;
|
||||||
uint64_t sent_size = rc->send_sizes.at(0);
|
uint64_t sent_size = rc->send_sizes.at(0);
|
||||||
rc->send_sizes.erase(rc->send_sizes.begin(), rc->send_sizes.begin()+1);
|
rc->send_sizes.erase(rc->send_sizes.begin(), rc->send_sizes.begin()+1);
|
||||||
|
if (!rdma_context->odp)
|
||||||
|
{
|
||||||
|
rc->send_done_pos += sent_size;
|
||||||
|
rc->send_out_full = false;
|
||||||
|
if (rc->send_done_pos == rc->send_out_size)
|
||||||
|
rc->send_done_pos = 0;
|
||||||
|
assert(rc->send_done_pos < rc->send_out_size);
|
||||||
|
}
|
||||||
int send_pos = 0, send_buf_pos = 0;
|
int send_pos = 0, send_buf_pos = 0;
|
||||||
while (sent_size > 0)
|
while (sent_size > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct msgr_rdma_context_t
|
||||||
ibv_device *dev = NULL;
|
ibv_device *dev = NULL;
|
||||||
ibv_device_attr_ex attrx;
|
ibv_device_attr_ex attrx;
|
||||||
ibv_pd *pd = NULL;
|
ibv_pd *pd = NULL;
|
||||||
|
bool odp = false;
|
||||||
ibv_mr *mr = NULL;
|
ibv_mr *mr = NULL;
|
||||||
ibv_comp_channel *channel = NULL;
|
ibv_comp_channel *channel = NULL;
|
||||||
ibv_cq *cq = NULL;
|
ibv_cq *cq = NULL;
|
||||||
|
@ -35,10 +36,16 @@ struct msgr_rdma_context_t
|
||||||
int max_cqe = 0;
|
int max_cqe = 0;
|
||||||
int used_max_cqe = 0;
|
int used_max_cqe = 0;
|
||||||
|
|
||||||
static msgr_rdma_context_t *create(const char *ib_devname, uint8_t ib_port, uint8_t gid_index, uint32_t mtu, int log_level);
|
static msgr_rdma_context_t *create(const char *ib_devname, uint8_t ib_port, uint8_t gid_index, uint32_t mtu, bool odp, int log_level);
|
||||||
~msgr_rdma_context_t();
|
~msgr_rdma_context_t();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct msgr_rdma_buf_t
|
||||||
|
{
|
||||||
|
void *buf = NULL;
|
||||||
|
ibv_mr *mr = NULL;
|
||||||
|
};
|
||||||
|
|
||||||
struct msgr_rdma_connection_t
|
struct msgr_rdma_connection_t
|
||||||
{
|
{
|
||||||
msgr_rdma_context_t *ctx = NULL;
|
msgr_rdma_context_t *ctx = NULL;
|
||||||
|
@ -50,8 +57,11 @@ struct msgr_rdma_connection_t
|
||||||
|
|
||||||
int send_pos = 0, send_buf_pos = 0;
|
int send_pos = 0, send_buf_pos = 0;
|
||||||
int next_recv_buf = 0;
|
int next_recv_buf = 0;
|
||||||
std::vector<void*> recv_buffers;
|
std::vector<msgr_rdma_buf_t> recv_buffers;
|
||||||
std::vector<uint64_t> send_sizes;
|
std::vector<uint64_t> send_sizes;
|
||||||
|
msgr_rdma_buf_t send_out;
|
||||||
|
int send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
|
||||||
|
bool send_out_full = false;
|
||||||
|
|
||||||
~msgr_rdma_connection_t();
|
~msgr_rdma_connection_t();
|
||||||
static msgr_rdma_connection_t *create(msgr_rdma_context_t *ctx, uint32_t max_send, uint32_t max_recv, uint32_t max_sge, uint32_t max_msg);
|
static msgr_rdma_connection_t *create(msgr_rdma_context_t *ctx, uint32_t max_send, uint32_t max_recv, uint32_t max_sge, uint32_t max_msg);
|
||||||
|
|
Loading…
Reference in New Issue