From: Jack Wang <[email protected]>

An old peer echoes REGISTER_RESULT.repeat unchanged but only sends one
result, so a batched source would read stale rkeys for the rest.
Negotiate RDMA_CAPABILITY_REG_BATCH like PIN_ALL and cap requests at
one chunk when unsupported; check resp.len instead of resp.repeat.

Signed-off-by: Jack Wang <[email protected]>
Signed-off-by: Dmitrii Omelchenko <[email protected]>
---
 migration/rdma.c       | 37 ++++++++++++++++++++++++++++++++++---
 migration/trace-events |  1 +
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/migration/rdma.c b/migration/rdma.c
index 863165de26..29a2480448 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -75,12 +75,15 @@ static inline uint64_t rdma_merge_max(void)
  * Capabilities for negotiation.
  */
 #define RDMA_CAPABILITY_PIN_ALL 0x01
+/* Peer replies to a batched REGISTER_REQUEST with one result per chunk. */
+#define RDMA_CAPABILITY_REG_BATCH 0x02
 
 /*
  * Add the other flags above to this list of known capabilities
  * as they are introduced.
  */
-static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL;
+static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL |
+                                     RDMA_CAPABILITY_REG_BATCH;
 
 /*
  * A work request ID is 64-bits and we split up these bits
@@ -352,6 +355,7 @@ typedef struct RDMAContext {
     uint64_t pend_length;         /* span length in bytes (within one chunk) */
 
     bool pin_all;
+    bool reg_batch;   /* peer negotiated RDMA_CAPABILITY_REG_BATCH */
 
     /*
      * infiniband-specific variables for opening the device
@@ -1947,6 +1951,14 @@ static int qemu_rdma_write_register(RDMAContext *rdma, 
RDMARegister *regs,
         return -1;
     }
 
+    /* resp.repeat is echoed back unchanged by old peers; resp.len is not. */
+    if (resp.len != count * sizeof(RDMARegisterResult)) {
+        error_setg(errp, "rdma: REGISTER_RESULT size mismatch: "
+                   "requested %u chunks (%zu bytes), peer returned %u bytes",
+                   count, count * sizeof(RDMARegisterResult), resp.len);
+        return -1;
+    }
+
     results = (RDMARegisterResult *)rdma->wr_data[reg_result_idx].control_curr;
     for (i = 0; i < count; i++) {
         RDMALocalBlock *block =
@@ -2172,6 +2184,8 @@ static int qemu_rdma_connect(RDMAContext *rdma, bool 
return_path,
         cap.flags |= RDMA_CAPABILITY_PIN_ALL;
     }
 
+    cap.flags |= RDMA_CAPABILITY_REG_BATCH;
+
     caps_to_network(&cap);
 
     ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY, errp);
@@ -2219,6 +2233,13 @@ static int qemu_rdma_connect(RDMAContext *rdma, bool 
return_path,
         rdma->pin_all = false;
     }
 
+    rdma->reg_batch = !!(cap.flags & RDMA_CAPABILITY_REG_BATCH);
+    if (!rdma->reg_batch) {
+        warn_report("RDMA: Server does not support batched chunk "
+                    "registration. Will register one chunk per request.");
+    }
+    trace_rdma_connect_reg_batch_outcome(rdma->reg_batch);
+
     trace_rdma_connect_pin_all_outcome(rdma->pin_all);
 
     rdma_ack_cm_event(cm_event);
@@ -2952,6 +2973,14 @@ static int qemu_rdma_write_run(RDMAContext *rdma, 
RDMASendRing *r,
                 return -1;
             }
         } else {
+            bool new_chunk = (int)cmd->block_index != last_bidx ||
+                             (int64_t)cmd->chunk_index != last_cidx;
+
+            /* Cap at one never-seen chunk per request without REG_BATCH. */
+            if (new_chunk && nreg == 1 && !rdma->reg_batch) {
+                break;
+            }
+
             /* Defer: record every span for a post-registration write. */
             reg_ring_idx[regn++] = pos + n;
             /*
@@ -2959,8 +2988,7 @@ static int qemu_rdma_write_run(RDMAContext *rdma, 
RDMASendRing *r,
              * non-decreasing within a run, so a repeat chunk is the last one
              * queued; add each chunk to the REGISTER batch only once.
              */
-            if ((int)cmd->block_index != last_bidx ||
-                (int64_t)cmd->chunk_index != last_cidx) {
+            if (new_chunk) {
                 regs[nreg].current_index = cmd->block_index;
                 regs[nreg].current_addr = addr;
                 nreg++;
@@ -3663,6 +3691,9 @@ static int qemu_rdma_accept(RDMAContext *rdma)
     if (cap.flags & RDMA_CAPABILITY_PIN_ALL) {
         rdma->pin_all = true;
     }
+    if (cap.flags & RDMA_CAPABILITY_REG_BATCH) {
+        rdma->reg_batch = true;
+    }
 
     rdma->cm_id = cm_event->id;
     verbs = cm_event->id->verbs;
diff --git a/migration/trace-events b/migration/trace-events
index e05ca23f32..3c5da0fd14 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -223,6 +223,7 @@ rdma_cleanup_disconnect(void) ""
 rdma_close(void) ""
 rdma_connect_pin_all_requested(void) ""
 rdma_connect_pin_all_outcome(bool pin) "%d"
+rdma_connect_reg_batch_outcome(bool reg_batch) "%d"
 rdma_dest_init_trying(const char *host, const char *ip) "%s => %s"
 rdma_dump_id_failed(const char *who) "%s RDMA Device opened, but can't query 
port information"
 rdma_dump_id(const char *who, const char *name, const char *dev_name, const 
char *dev_path, const char *ibdev_path, int transport, const char 
*transport_name) "%s RDMA Device opened: kernel name %s uverbs device name %s, 
infiniband_verbs class device path %s, infiniband class device path %s, 
transport: (%d) %s"
-- 
2.39.5


Reply via email to