pyverbs: Update MW rkey after type 2 bind

set_bind_wr() assigns the incremented rkey to the bind WR but leaves MW.rkey
unchanged. After the bind succeeds, applications that publish MW.rkey to the
peer send the old rkey, while the NIC has already bound the MW with the new
rkey. Remote access using the stale rkey consequently fails with a remote
access error.

Synchronize MW.rkey with the rkey of successfully posted IBV_WR_BIND_MW
requests. When posting a WR list partially fails, only Bind WRs before bad_wr
are updated, since those are the requests accepted by ibv_post_send().

Signed-off-by: Zhiwei Zhang <zhangzhiwei@bitintelligence.io>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
diff --git a/pyverbs/qp.pyx b/pyverbs/qp.pyx
index a14fd93..abf8635 100644
--- a/pyverbs/qp.pyx
+++ b/pyverbs/qp.pyx
@@ -33,6 +33,19 @@
     unsigned long htobe32(unsigned long host_32bits)
 
 
+cdef void update_mw_rkeys(v.ibv_send_wr *wr, v.ibv_send_wr *bad_wr):
+    """
+    Update MW rkeys for successfully posted bind WRs.
+
+    ibv_post_send() returns the first WR that was not posted in bad_wr, so
+    only the WRs preceding it were accepted when posting a list fails.
+    """
+    while wr != bad_wr:
+        if wr.opcode == e.IBV_WR_BIND_MW:
+            wr.bind_mw.mw.rkey = wr.bind_mw.rkey
+        wr = wr.next
+
+
 cdef class QPCap(PyverbsObject):
     def __init__(self, max_send_wr=1, max_recv_wr=10, max_send_sge=1,
                  max_recv_sge=1, max_inline_data=0):
@@ -1286,8 +1299,9 @@
         """
         # In order to provide a pointer to a pointer, use a temporary cdef'ed
         # variable.
-        cdef v.ibv_send_wr *my_bad_wr
+        cdef v.ibv_send_wr *my_bad_wr = NULL
         rc = v.ibv_post_send(self.qp, &wr.send_wr, &my_bad_wr)
+        update_mw_rkeys(&wr.send_wr, my_bad_wr)
         if rc != 0:
             if (bad_wr):
                 memcpy(&bad_wr.send_wr, my_bad_wr, sizeof(bad_wr.send_wr))
diff --git a/tests/test_mr.py b/tests/test_mr.py
index ef75f4b..b7ef04e 100644
--- a/tests/test_mr.py
+++ b/tests/test_mr.py
@@ -317,9 +317,11 @@
         # Poll the bind MW WR.
         u.poll_cq(self.server.cq)
         u.poll_cq(self.client.cq)
-        self.server.rkey = client_send_wr.rkey
+        self.assertEqual(self.client.mw.rkey, client_send_wr.rkey)
+        self.assertEqual(self.server.mw.rkey, server_send_wr.rkey)
+        self.server.rkey = self.client.mw.rkey
         self.server.raddr = self.client.mr.buf
-        self.client.rkey = server_send_wr.rkey
+        self.client.rkey = self.server.mw.rkey
         self.client.raddr = self.server.mr.buf
 
     def invalidate_mw_type1(self):