File: test_mlx5_rdmacm.py

package info (click to toggle)
rdma-core 61.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,124 kB
  • sloc: ansic: 176,798; python: 15,496; sh: 2,742; perl: 1,465; makefile: 73
file content (208 lines) | stat: -rw-r--r-- 8,797 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
# Copyright (c) 2020 Nvidia Corporation. All rights reserved. See COPYING file

import unittest
import errno

from pyverbs.providers.mlx5.mlx5dv import Mlx5DVQPInitAttr, Mlx5QP, \
    Mlx5DVDCInitAttr, Mlx5Context
from tests.test_rdmacm import CMAsyncConnection
from tests.mlx5_base import Mlx5PyverbsAPITestCase, Mlx5RDMACMBaseTest
from pyverbs.pyverbs_error import PyverbsRDMAError
from pyverbs.srq import SRQ, SrqInitAttr, SrqAttr
from pyverbs.providers.mlx5.mlx5_enums import mlx5dv_qp_init_attr_mask, mlx5dv_dc_type
from tests.base_rdmacm import AsyncCMResources
from pyverbs.qp import QPCap, QPInitAttrEx
from pyverbs.cmid import ConnParam
from tests.base import DCT_KEY
from pyverbs.addr import AH
from pyverbs.libibverbs_enums import ibv_wr_opcode, ibv_qp_create_send_ops_flags, ibv_qp_init_attr_mask, \
    ibv_qp_type, ibv_qp_state
from pyverbs.cq import CQ
import tests.utils as u


class DcCMConnection(CMAsyncConnection):
    """
    Implement RDMACM connection management for asynchronous CMIDs using DC as
    an external QP.
    """
    def create_cm_res(self, ip_addr, passive, **kwargs):
        self.cm_res = DcCMResources(addr=ip_addr, passive=passive, **kwargs)
        if passive:
            self.cm_res.create_cmid()
        else:
            for conn_idx in range(self.num_conns):
                self.cm_res.create_cmid(conn_idx)

    def _ext_qp_server_traffic(self):
        recv_wr = u.get_recv_wr(self.cm_res)
        for _ in range(self.cm_res.num_msgs):
            u.post_recv(self.cm_res, recv_wr)
        self.syncer.wait()
        for _ in range(self.cm_res.num_msgs):
            u.poll_cq(self.cm_res.cq)

    def _ext_qp_client_traffic(self):
        self.cm_res.remote_dct_num = self.cm_res.remote_qpn
        _, send_wr = u.get_send_elements(self.cm_res, self.cm_res.passive)
        ah = AH(self.cm_res.cmid.pd, attr=self.cm_res.remote_ah)
        self.syncer.wait()
        for send_idx in range(self.cm_res.num_msgs):
            dci_idx = send_idx % len(self.cm_res.qps)
            u.post_send_ex(self.cm_res, send_wr, ibv_wr_opcode.IBV_WR_SEND, ah=ah,
                           qp_idx=dci_idx)
            u.poll_cq(self.cm_res.cq)

    def disconnect(self):
        if self.cm_res.reserved_qp_num and self.cm_res.passive:
            Mlx5Context.reserved_qpn_dealloc(self.cm_res.child_id.context,
                                             self.cm_res.reserved_qp_num)
            self.cm_res.reserved_qp_num = 0
        super().disconnect()


class DcCMResources(AsyncCMResources):
    """
    DcCMResources class contains resources for RDMA CM asynchronous
    communication using DC as an external QP.
    """
    def __init__(self, addr=None, passive=None, **kwargs):
        """
        Init DcCMResources instance.
        :param addr: Local address to bind to.
        :param passive: Indicate if this CM is the passive CM.
        """
        super().__init__(addr=addr, passive=passive, **kwargs)
        self.srq = None
        self.remote_dct_num = None
        self.reserved_qp_num = 0

    def create_qp(self, conn_idx=0):
        """
        Create an RDMACM QP. If self.with_ext_qp is set, then an external CQ and
        DC QP will be created. In case that CQ is already created, it is used
        for the newly created QP.
        """
        try:
            if not self.passive:
                # Create the DCI QPs.
                cmid = self.cmids[conn_idx]
                self.create_cq(cmid)
                qp_init_attr = self.create_qp_init_attr(cmid, ibv_qp_create_send_ops_flags.IBV_QP_EX_WITH_SEND)
                attr = Mlx5DVQPInitAttr(comp_mask=mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_DC,
                                        dc_init_attr=Mlx5DVDCInitAttr())
                self.qps[conn_idx] = Mlx5QP(cmid.context, qp_init_attr, attr)

            if self.passive and conn_idx == 0:
                # Create the DCT QP only for the first connection.
                cmid = self.child_id
                self.create_cq(cmid)
                self.create_srq(cmid)
                qp_init_attr = self.create_qp_init_attr(cmid)
                dc_attr = Mlx5DVDCInitAttr(dc_type=mlx5dv_dc_type.MLX5DV_DCTYPE_DCT,
                                           dct_access_key=DCT_KEY)
                attr = Mlx5DVQPInitAttr(comp_mask=mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_DC,
                                        dc_init_attr=dc_attr)
                self.qps[conn_idx] = Mlx5QP(cmid.context, qp_init_attr, attr)
        except PyverbsRDMAError as ex:
            if ex.error_code == errno.EOPNOTSUPP:
                raise unittest.SkipTest('Create DC QP is not supported')
            raise ex

    def create_qp_cap(self):
        return QPCap(self.num_msgs, 0, 1, 0)

    def create_qp_init_attr(self, cmid, send_ops_flags=0):
        comp_mask = ibv_qp_init_attr_mask.IBV_QP_INIT_ATTR_PD
        if send_ops_flags:
            comp_mask |= ibv_qp_init_attr_mask.IBV_QP_INIT_ATTR_SEND_OPS_FLAGS
        return QPInitAttrEx(cap=self.create_qp_cap(), pd=cmid.pd, scq=self.cq,
                            rcq=self.cq, srq=self.srq, qp_type=ibv_qp_type.IBV_QPT_DRIVER,
                            send_ops_flags=send_ops_flags, comp_mask=comp_mask,
                            sq_sig_all=1)

    def create_srq(self, cmid):
        srq_init_attr = SrqInitAttr(SrqAttr(max_wr=self.num_msgs))
        try:
            self.srq = SRQ(cmid.pd, srq_init_attr)
        except PyverbsRDMAError as ex:
            if ex.error_code == errno.EOPNOTSUPP:
                raise unittest.SkipTest('Create SRQ is not supported')
            raise ex

    def modify_ext_qp_to_rts(self, conn_idx=0):
        cmids = self.child_ids if self.passive else self.cmids
        if not self.passive or not conn_idx:
            qp = self.qps[conn_idx]
            attr, _ = cmids[conn_idx].init_qp_attr(ibv_qp_state.IBV_QPS_INIT)
            qp.to_init(attr)
            attr, _ = cmids[conn_idx].init_qp_attr(ibv_qp_state.IBV_QPS_RTR)
            qp.to_rtr(attr)
            if not self.passive:
                # The passive QP is DCT which should stay in RTR state.
                self.remote_ah = attr.ah_attr
                attr, _ = cmids[conn_idx].init_qp_attr(ibv_qp_state.IBV_QPS_RTS)
                qp.to_rts(attr)

    def create_conn_param(self, qp_num=0, conn_idx=0):
        if conn_idx and self.passive:
            try:
                ctx = self.child_id.context
                self.reserved_qp_num = Mlx5Context.reserved_qpn_alloc(ctx)
            except PyverbsRDMAError as ex:
                if ex.error_code == errno.EOPNOTSUPP:
                    raise unittest.SkipTest('Alloc reserved QP number is not supported')
                raise ex
            qp_num = self.reserved_qp_num
        else:
            qp_num = self.qps[conn_idx].qp_num
        return ConnParam(qp_num=qp_num)


class Mlx5CMTestCase(Mlx5RDMACMBaseTest):
    """
    Mlx5 RDMACM test class.
    """
    def test_rdmacm_async_traffic_dc_external_qp(self):
        """
        Connect multiple RDMACM connections using DC as an external QP for
        traffic.
        """
        self.two_nodes_rdmacm_traffic(DcCMConnection, self.rdmacm_traffic,
                                      with_ext_qp=True, num_conns=2)


class ReservedQPTest(Mlx5PyverbsAPITestCase):

    def test_reservered_qpn(self):
        """
        Alloc reserved qpn multiple times and then dealloc the qpns. In addition,
        the test includes bad flows where a fake qpn gets deallocated, and a
        real qpn gets deallocated twice.
        """
        try:
            # Alloc qp number multiple times.
            qpns = []
            for i in range(1000):
                qpns.append(Mlx5Context.reserved_qpn_alloc(self.ctx))
            for i in range(1000):
                Mlx5Context.reserved_qpn_dealloc(self.ctx, qpns[i])

            # Dealloc qp number that was not allocated.
            qpn = Mlx5Context.reserved_qpn_alloc(self.ctx)
            with self.assertRaises(PyverbsRDMAError) as ex:
                fake_qpn = qpn - 1
                Mlx5Context.reserved_qpn_dealloc(self.ctx, fake_qpn)
            self.assertEqual(ex.exception.error_code, errno.EINVAL)

            # Try to dealloc same qp number twice.
            Mlx5Context.reserved_qpn_dealloc(self.ctx, qpn)
            with self.assertRaises(PyverbsRDMAError) as ex:
                Mlx5Context.reserved_qpn_dealloc(self.ctx, qpn)
            self.assertEqual(ex.exception.error_code, errno.EINVAL)

        except PyverbsRDMAError as ex:
            if ex.error_code == errno.EOPNOTSUPP:
                raise unittest.SkipTest('Alloc reserved QP number is not supported')
            raise ex