RAM writes and control messages share the send queue. If outstanding writes fill it, RDMA writes drain a completion and retry, but control sends fail the migration.
Move send posting and queue-full handling into a common helper. On ENOMEM, drain one outstanding RDMA write and retry the failed request once. If no write is outstanding, or the retry still fails, propagate the error. Signed-off-by: Yanfei Xu <[email protected]> --- migration/rdma.c | 78 +++++++++++++++++++++++++++--------------- migration/trace-events | 2 +- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 6e8436ccc1..ee6da4f137 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -1530,6 +1530,52 @@ err_block_for_wrid: return -1; } +/* + * Post a send work request, draining an outstanding RDMA write if the send + * queue is full. + */ +static int qemu_rdma_post_send(RDMAContext *rdma, + struct ibv_send_wr *send_wr, + Error **errp) +{ + struct ibv_send_wr *bad_wr; + uint64_t wr_id = send_wr->wr_id & RDMA_WRID_TYPE_MASK; + const char *wr_desc; + int ret; + + switch (wr_id) { + case RDMA_WRID_RDMA_WRITE: + wr_desc = "RDMA write"; + break; + case RDMA_WRID_SEND_CONTROL: + wr_desc = "control send"; + break; + default: + wr_desc = "send work request"; + break; + } + + ret = ibv_post_send(rdma->qp, send_wr, &bad_wr); + if (ret == ENOMEM && rdma->nb_sent) { + trace_qemu_rdma_post_send_queue_full(send_wr->wr_id, rdma->nb_sent); + ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); + if (ret < 0) { + error_setg(errp, "rdma migration: failed to make room for %s", + wr_desc); + return -1; + } + ret = ibv_post_send(rdma->qp, send_wr, &bad_wr); + } + + if (ret > 0) { + error_setg_errno(errp, ret, "rdma migration: post %s failed", + wr_desc); + return -1; + } + + return 0; +} + /* * Post a SEND message work request for the control channel * containing some data and block until the post completes. @@ -1540,7 +1586,6 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, { int ret; RDMAWorkRequestData *wr = &rdma->wr_data[RDMA_WRID_CONTROL]; - struct ibv_send_wr *bad_wr; struct ibv_sge sge = { .addr = (uintptr_t)(wr->control), .length = head->len + sizeof(RDMAControlHeader), @@ -1572,11 +1617,8 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, memcpy(wr->control + sizeof(RDMAControlHeader), buf, head->len); } - - ret = ibv_post_send(rdma->qp, &send_wr, &bad_wr); - - if (ret > 0) { - error_setg(errp, "Failed to use post IB SEND for control"); + ret = qemu_rdma_post_send(rdma, &send_wr, errp); + if (ret < 0) { return -1; } @@ -1836,7 +1878,6 @@ static int qemu_rdma_write_one(RDMAContext *rdma, { struct ibv_sge sge; struct ibv_send_wr send_wr = { 0 }; - struct ibv_send_wr *bad_wr; int reg_result_idx, ret, count = 0; uint64_t chunk, chunks; uint64_t chunk_size = migrate_rdma_chunk_size(); @@ -1850,7 +1891,6 @@ static int qemu_rdma_write_one(RDMAContext *rdma, .repeat = 1, }; -retry: sge.addr = (uintptr_t)(block->local_host_addr + (current_addr - block->offset)); sge.length = length; @@ -2020,26 +2060,8 @@ retry: trace_qemu_rdma_write_one_post(chunk, sge.addr, send_wr.wr.rdma.remote_addr, sge.length); - /* - * ibv_post_send() does not return negative error numbers, - * per the specification they are positive - no idea why. - */ - ret = ibv_post_send(rdma->qp, &send_wr, &bad_wr); - - if (ret == ENOMEM) { - trace_qemu_rdma_write_one_queue_full(); - ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); - if (ret < 0) { - error_setg(errp, "rdma migration: failed to make " - "room in full send queue!"); - return -1; - } - - goto retry; - - } else if (ret > 0) { - error_setg_errno(errp, ret, - "rdma migration: post rdma write failed"); + ret = qemu_rdma_post_send(rdma, &send_wr, errp); + if (ret < 0) { return -1; } diff --git a/migration/trace-events b/migration/trace-events index af0e784535..bc0c167a1a 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -251,7 +251,7 @@ qemu_rdma_unregister_waiting_complete(uint64_t chunk) "Unregister for chunk: %" qemu_rdma_write_flush(int sent) "sent total: %d" qemu_rdma_write_one_block(int count, int block, uint64_t chunk, uint64_t current, uint64_t len, int nb_sent, int nb_chunks) "(%d) Not clobbering: block: %d chunk %" PRIu64 " current %" PRIu64 " len %" PRIu64 " %d %d" qemu_rdma_write_one_post(uint64_t chunk, long addr, long remote, uint32_t len) "Posting chunk: %" PRIu64 ", addr: 0x%lx remote: 0x%lx, bytes %" PRIu32 -qemu_rdma_write_one_queue_full(void) "" +qemu_rdma_post_send_queue_full(uint64_t wr_id, int sent) "send queue full, wr_id=%" PRIu64 ", outstanding writes=%d" qemu_rdma_write_one_recvregres(int mykey, int theirkey, uint64_t chunk) "Received registration result: my key: 0x%x their key 0x%x, chunk %" PRIu64 qemu_rdma_write_one_sendreg(uint64_t chunk, int len, int index, int64_t offset) "Sending registration request chunk %" PRIu64 " for %d bytes, index: %d, offset: %" PRId64 qemu_rdma_write_one_top(uint64_t chunks, uint64_t size) "Writing %" PRIu64 " chunks, (%" PRIu64 " MB)" -- 2.20.1
