On Sun, Aug 23, 2026 at 4:18 AM Yanfei Xu <[email protected]> wrote: > > 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]> > Reviewed-by: Peter Xu <[email protected]> Reviewed-by: Jack Wang <[email protected]> > --- > migration/rdma.c | 75 ++++++++++++++++++++++++++---------------- > migration/trace-events | 2 +- > 2 files changed, 48 insertions(+), 29 deletions(-) > > diff --git a/migration/rdma.c b/migration/rdma.c > index b79f4263f7..b7580ab54f 100644 > --- a/migration/rdma.c > +++ b/migration/rdma.c > @@ -1553,6 +1553,51 @@ err_block_for_wrid: > return -1; > } > > +/* > + * Post a send work request, draining an outstanding RDMA write if the send > + * queue is full. > + */ > +static bool 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: > + g_assert_not_reached(); > + } > + > + 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 false; > + } > + 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 false; > + } > + > + return true; > +} > + > /* > * Post a SEND message work request for the control channel > * containing some data and block until the post completes. > @@ -1563,7 +1608,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), > @@ -1595,11 +1639,7 @@ 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"); > + if (!qemu_rdma_post_send(rdma, &send_wr, errp)) { > return -1; > } > > @@ -1859,7 +1899,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; > uint64_t chunk, chunks; > uint64_t chunk_size = migrate_rdma_chunk_size(); > @@ -1873,7 +1912,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; > @@ -2026,26 +2064,7 @@ 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"); > + if (!qemu_rdma_post_send(rdma, &send_wr, errp)) { > return -1; > } > > diff --git a/migration/trace-events b/migration/trace-events > index bcc102e323..7197c9dc6d 100644 > --- a/migration/trace-events > +++ b/migration/trace-events > @@ -250,7 +250,7 @@ qemu_rdma_unregister_waiting_send(uint64_t chunk) > "Sending unregister for chunk: > qemu_rdma_unregister_waiting_complete(uint64_t chunk) "Unregister for chunk: > %" PRIu64 " complete." > qemu_rdma_write_flush(int sent) "sent total: %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
