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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
|
# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
# Copyright (c) 2020 NVIDIA Corporation . All rights reserved. See COPYING file
import unittest
import resource
import random
import struct
import errno
import math
import time
import sys
from pyverbs.providers.mlx5.mlx5dv import Mlx5Context, Mlx5DVContextAttr, \
Mlx5DVQPInitAttr, Mlx5QP, Mlx5DVDCInitAttr, Mlx5DCIStreamInitAttr, \
Mlx5DevxObj, Mlx5UMEM, Mlx5UAR, WqeDataSeg, WqeCtrlSeg, Wqe, Mlx5Cqe64, \
Mlx5DVCQInitAttr, Mlx5CQ
from tests.base import RoCETrafficResources, set_rnr_attributes, DCT_KEY, \
RDMATestCase, PyverbsAPITestCase, RDMACMBaseTest, BaseResources, PATH_MTU, \
RNR_RETRY, RETRY_CNT, MIN_RNR_TIMER, TIMEOUT, MAX_RDMA_ATOMIC, RCResources, \
is_gid_available
from pyverbs.pyverbs_error import PyverbsRDMAError, PyverbsUserError, \
PyverbsError
from pyverbs.providers.mlx5.mlx5dv_objects import Mlx5DvObj
from pyverbs.qp import QPCap, QPInitAttrEx, QPAttr
from pyverbs.providers.mlx5.mlx5_enums import mlx5dv_qp_init_attr_mask, mlx5dv_dc_type, mlx5dv_qp_init_attr_mask, \
mlx5dv_context_attr_flags, mlx5dv_obj_type, mlx5dv_context_attr_comp_mask, MLX5_SEND_WQE_BB, MLX5_SEND_WQE_SHIFT, \
MLX5_WQE_CTRL_CQ_UPDATE, MLX5_OPCODE_SEND_IMM, MLX5_SND_DBR, MLX5_RCV_DBR, MLX5_CQE_INVALID, MLX5_CQE_RESP_ERR, \
MLX5_CQE_REQ_ERR, MLX5_CQE_REQ, MLX5_CQE_RESP_SEND_IMM, _MLX5DV_UAR_ALLOC_TYPE_NC
from pyverbs.addr import AHAttr, GlobalRoute
from pyverbs.cq import CqInitAttrEx
from pyverbs.device import FdArr
import pyverbs.mem_alloc as mem
import pyverbs.dma_util as dma
import pyverbs.device as d
from pyverbs.pd import PD
from pyverbs.libibverbs_enums import ibv_qp_init_attr_mask, ibv_qp_type, ibv_qp_state, \
ibv_access_flags, ibv_qp_attr_mask, ibv_wc_status, ibv_rereg_mr_flags, ibv_wr_opcode, \
ibv_gid_type_sysfs, ibv_qp_create_send_ops_flags
from pyverbs.mr import MR
import tests.utils
MLX5_CQ_SET_CI = 0
POLL_CQ_TIMEOUT = 5 # In seconds
PORT_STATE_TIMEOUT = 20 # In seconds
MELLANOX_VENDOR_ID = 0x02c9
MLX5_DEVS = {
0x1011, # MT4113 Connect-IB
0x1012, # Connect-IB Virtual Function
0x1013, # ConnectX-4
0x1014, # ConnectX-4 Virtual Function
0x1015, # ConnectX-4LX
0x1016, # ConnectX-4LX Virtual Function
0x1017, # ConnectX-5, PCIe 3.0
0x1018, # ConnectX-5 Virtual Function
0x1019, # ConnectX-5 Ex
0x101a, # ConnectX-5 Ex VF
0x101b, # ConnectX-6
0x101c, # ConnectX-6 VF
0x101d, # ConnectX-6 DX
0x101e, # ConnectX family mlx5Gen Virtual Function
0x101f, # ConnectX-6 LX
0x1021, # ConnectX-7
0x1023, # ConnectX-8
0x1025, # ConnectX-9
0x1027, # ConnectX-10
0xa2d2, # BlueField integrated ConnectX-5 network controller
0xa2d3, # BlueField integrated ConnectX-5 network controller VF
0xa2d6, # BlueField-2 integrated ConnectX-6 Dx network controller
0xa2dc, # BlueField-3 integrated ConnectX-7 network controller
0xa2df, # BlueField-4 integrated ConnectX-8 network controller
}
IB_SMP_ATTR_PORT_INFO = 0x0015
IB_MGMT_CLASS_SUBN_LID_ROUTED = 0x01
IB_MGMT_METHOD_GET = 0x01
DB_BF_DBR_LESS_BUF_OFFSET = 0x600
class PortStatus:
MLX5_PORT_UP = 1
MLX5_PORT_DOWN = 2
class PortState:
NO_STATE_CHANGE = 0
DOWN = 1
INIT = 2
ARMED = 3
ACTIVE = 4
def is_mlx5_dev(ctx):
dev_attrs = ctx.query_device()
return dev_attrs.vendor_id == MELLANOX_VENDOR_ID and \
dev_attrs.vendor_part_id in MLX5_DEVS
def skip_if_not_mlx5_dev(ctx):
if not is_mlx5_dev(ctx):
raise unittest.SkipTest('Can not run the test over non MLX5 device')
class Mlx5PyverbsAPITestCase(PyverbsAPITestCase):
def setUp(self):
super().setUp()
skip_if_not_mlx5_dev(self.ctx)
class Mlx5RDMATestCase(RDMATestCase):
def setUp(self):
super().setUp()
skip_if_not_mlx5_dev(d.Context(name=self.dev_name))
class Mlx5RDMACMBaseTest(RDMACMBaseTest):
def setUp(self):
super().setUp()
skip_if_not_mlx5_dev(d.Context(name=self.dev_name))
class Mlx5DcResources(RoCETrafficResources):
def __init__(self, dev_name, ib_port, gid_index, send_ops_flags,
qp_count=1, create_flags=0, mr_access=None):
self.send_ops_flags = send_ops_flags
self.create_flags = create_flags
if mr_access is None:
self.mr_access = (ibv_access_flags.IBV_ACCESS_REMOTE_WRITE |
ibv_access_flags.IBV_ACCESS_LOCAL_WRITE |
ibv_access_flags.IBV_ACCESS_REMOTE_ATOMIC |
ibv_access_flags.IBV_ACCESS_REMOTE_READ)
else:
self.mr_access = mr_access
super().__init__(dev_name, ib_port, gid_index, with_srq=True,
qp_count=qp_count)
def to_rts(self):
attr = self.create_qp_attr()
for i in range(self.qp_count):
self.qps[i].to_rts(attr)
self.dct_qp.to_rtr(attr)
def create_context(self):
mlx5dv_attr = Mlx5DVContextAttr()
try:
self.ctx = Mlx5Context(mlx5dv_attr, name=self.dev_name)
except PyverbsUserError as ex:
raise unittest.SkipTest(f'Could not open mlx5 context ({ex})')
except PyverbsRDMAError:
raise unittest.SkipTest('Opening mlx5 context is not supported')
def create_mr(self):
self.mr = MR(self.pd, self.msg_size, self.mr_access)
def create_qp_cap(self):
return QPCap(100, 0, 1, 0)
def create_qp_attr(self):
qp_attr = QPAttr(port_num=self.ib_port)
set_rnr_attributes(qp_attr)
qp_access = ibv_access_flags.IBV_ACCESS_LOCAL_WRITE | ibv_access_flags.IBV_ACCESS_REMOTE_WRITE | \
ibv_access_flags.IBV_ACCESS_REMOTE_ATOMIC | ibv_access_flags.IBV_ACCESS_REMOTE_READ
qp_attr.qp_access_flags = qp_access
gr = GlobalRoute(dgid=self.ctx.query_gid(self.ib_port, self.gid_index),
sgid_index=self.gid_index)
ah_attr = AHAttr(port_num=self.ib_port, is_global=1, gr=gr,
dlid=self.port_attr.lid)
qp_attr.ah_attr = ah_attr
return qp_attr
def create_qp_init_attr(self, 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=self.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_qps(self):
# Create the DCI QPs.
qp_init_attr = self.create_qp_init_attr(self.send_ops_flags)
try:
for _ in range(self.qp_count):
comp_mask = mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_DC
if self.create_flags:
comp_mask |= mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_QP_CREATE_FLAGS
attr = Mlx5DVQPInitAttr(comp_mask=comp_mask,
create_flags=self.create_flags,
dc_init_attr=Mlx5DVDCInitAttr())
qp = Mlx5QP(self.ctx, qp_init_attr, attr)
self.qps.append(qp)
self.qps_num.append(qp.qp_num)
self.psns.append(random.getrandbits(24))
# Create the DCT QP.
qp_init_attr = self.create_qp_init_attr()
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.dct_qp = Mlx5QP(self.ctx, qp_init_attr, attr)
except PyverbsRDMAError as ex:
if ex.error_code == errno.EOPNOTSUPP:
raise unittest.SkipTest(f'Create DC QP is not supported')
raise ex
class Mlx5DcStreamsRes(Mlx5DcResources):
def __init__(self, dev_name, ib_port, gid_index, send_ops_flags,
qp_count=1, create_flags=0, mr_access=None):
super().__init__(dev_name, ib_port, gid_index, send_ops_flags,
qp_count, create_flags, mr_access)
def reset_qp(self, qp_idx):
qp_attr = QPAttr(qp_state=ibv_qp_state.IBV_QPS_RESET)
self.qps[qp_idx].modify(qp_attr, ibv_qp_attr_mask.IBV_QP_STATE)
self.qps[qp_idx].to_rts(qp_attr)
def get_stream_id(self, qp_idx):
return self.current_qp_stream_id[qp_idx]
def generate_stream_id(self, qp_idx):
self.current_qp_stream_id[qp_idx] += 1
# Reset stream id to check double-usage
if self.current_qp_stream_id[qp_idx] > self.dcis[qp_idx]['stream']+2:
self.current_qp_stream_id[qp_idx] = 1
return self.current_qp_stream_id[qp_idx]
def dci_reset_stream_id(self, qp_idx):
stream_id = self.get_stream_id(qp_idx)
Mlx5QP.modify_dci_stream_channel_id(self.qps[qp_idx], stream_id)
def generate_dci_attr(self, qpn):
# This array contains current number of log_dci_streams
# and log_dci_errored values per qp. For 1-st qp number
# of streams greater than number of errored and vice-versa
# for the 2nd qp.
qp_arr = {0: [3, 2], 1: [2, 3]}
try:
dci_caps = self.ctx.query_mlx5_device().dci_streams_caps
except PyverbsRDMAError as ex:
if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]:
raise unittest.SkipTest('Get DCI caps is not supported')
raise ex
if not dci_caps or dci_caps['max_log_num_concurent'] == 0:
raise unittest.SkipTest('DCI caps is not supported by HW')
self.log_dci_streams = min(qp_arr.get(qpn, [1,1])[0],
dci_caps['max_log_num_concurent'])
self.log_dci_errored = min(qp_arr.get(qpn, [1,1])[1],
dci_caps['max_log_num_errored'])
def create_qps(self):
# Create the DCI QPs.
qp_init_attr = self.create_qp_init_attr(self.send_ops_flags)
self.dcis = {}
# This array contains current stream id
self.current_qp_stream_id = {}
comp_mask = mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_DC | \
mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_DCI_STREAMS
try:
for qpn in range(self.qp_count):
if self.create_flags:
comp_mask |= mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_QP_CREATE_FLAGS
self.generate_dci_attr(qpn)
stream_ctx = Mlx5DCIStreamInitAttr(self.log_dci_streams,
self.log_dci_errored)
self.dcis[qpn] = {'stream': 1 << self.log_dci_streams,
'errored': 1 << self.log_dci_errored}
attr = Mlx5DVQPInitAttr(comp_mask=comp_mask,
create_flags=self.create_flags,
dc_init_attr=Mlx5DVDCInitAttr(dci_streams=stream_ctx))
qp = Mlx5QP(self.ctx, qp_init_attr, attr)
self.qps.append(qp)
# Different values for start point of stream id per qp
self.current_qp_stream_id[qpn] = qpn
self.qps_num.append(qp.qp_num)
self.psns.append(random.getrandbits(24))
# Create the DCT QP.
qp_init_attr = self.create_qp_init_attr()
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.dct_qp = Mlx5QP(self.ctx, qp_init_attr, attr)
except PyverbsRDMAError as ex:
if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]:
raise unittest.SkipTest('Create DC QP is not supported')
raise ex
class WqAttrs:
def __init__(self):
super().__init__()
self.wqe_num = 0
self.wqe_size = 0
self.wq_size = 0
self.head = 0
self.post_idx = 0
self.wqe_shift = 0
self.offset = 0
def __str__(self):
return str(vars(self))
def __format__(self, format_spec):
return str(self).__format__(format_spec)
class CqAttrs:
def __init__(self):
super().__init__()
self.cons_idx = 0
self.cqe_size = 64
self.ncqes = 256
def __str__(self):
return str(vars(self))
def __format__(self, format_spec):
return str(self).__format__(format_spec)
class QueueAttrs:
def __init__(self):
self.rq = WqAttrs()
self.sq = WqAttrs()
self.cq = CqAttrs()
def __str__(self):
print_format = '{}:\n\t{}\n'
return print_format.format('RQ Attributes', self.rq) + \
print_format.format('SQ Attributes', self.sq) + \
print_format.format('CQ Attributes', self.cq)
class Mlx5DevxRcResources(BaseResources):
"""
Creates all the DevX resources needed for a traffic-ready RC DevX QP,
including methods to transit the WQs into RTS state.
It also includes traffic methods for post send/receive and poll.
The class currently supports post send with immediate, but can be
easily extended to support other opcodes in the future.
"""
def __init__(self, dev_name, ib_port, gid_index, msg_size=1024, activate_port_state=False,
send_dbr_mode=0):
from tests.mlx5_prm_structs import SendDbrMode
super().__init__(dev_name, ib_port, gid_index)
self.umems = {}
self.send_dbr_mode = send_dbr_mode
self.msg_size = msg_size
self.num_msgs = 1000
self.imm = 0x03020100
self.uar = {}
self.max_recv_sge = 1
self.eqn = None
self.pd = None
self.dv_pd = None
self.mr = None
self.msi_vector = None
self.eq = None
self.cq = None
self.qp = None
self.qpn = None
self.psn = None
self.lid = None
self.gid = [0, 0, 0, 0]
# Remote attrs
self.rqpn = None
self.rpsn = None
self.rlid = None
self.rgid = [0, 0, 0, 0]
self.rmac = None
self.devx_objs = []
self.qattr = QueueAttrs()
self.with_odp = False
self.user_addr = None
if activate_port_state:
start_state_t = time.perf_counter()
self.change_port_state_with_registers(PortStatus.MLX5_PORT_UP)
admin_status, oper_status = self.query_port_state_with_registers()
while admin_status != PortStatus.MLX5_PORT_UP or oper_status != PortStatus.MLX5_PORT_UP:
if time.perf_counter() - start_state_t >= PORT_STATE_TIMEOUT:
raise PyverbsRDMAError('Could not change the port state to UP')
self.change_port_state_with_registers(PortStatus.MLX5_PORT_UP)
admin_status, oper_status = self.query_port_state_with_registers()
time.sleep(1)
mad_port_state = self.query_port_state_with_mads(ib_port)
while mad_port_state < PortState.ACTIVE:
if time.perf_counter() - start_state_t >= PORT_STATE_TIMEOUT:
raise PyverbsRDMAError('Could not change the port state to UP')
time.sleep(1)
mad_port_state = self.query_port_state_with_mads(ib_port)
if self.send_dbr_mode != SendDbrMode.DBR_VALID:
self.check_cap_send_dbr_mode()
self.init_resources()
def get_wqe_data_segment(self):
return WqeDataSeg(self.mr.length, self.mr.lkey, self.mr.buf)
def change_port_state_with_registers(self, state):
from tests.mlx5_prm_structs import PaosReg
paos_in = PaosReg(local_port=self.ib_port, admin_status=state, ase=1)
self.access_paos_register(paos_in)
def query_port_state_with_registers(self):
from tests.mlx5_prm_structs import PaosReg
paos_in = PaosReg(local_port=self.ib_port)
paos_out = self.access_paos_register(paos_in)
return paos_out.admin_status, paos_out.oper_status
def access_paos_register(self, paos_in, op_mod=0): # op_mod: 0 - write / 1 - read
from tests.mlx5_prm_structs import AccessPaosRegisterIn, \
AccessPaosRegisterOut, DevxOps
paos_reg_in = AccessPaosRegisterIn(op_mod=op_mod,
register_id=DevxOps.MLX5_CMD_OP_ACCESS_REGISTER_PAOS,
data=paos_in)
cmd_out = self.ctx.devx_general_cmd(paos_reg_in, len(AccessPaosRegisterOut()))
paos_reg_out = AccessPaosRegisterOut(cmd_out)
if paos_reg_out.status:
raise PyverbsRDMAError(f'Failed to access PAOS register ({paos_reg_out.syndrome})')
return paos_reg_out.data
def query_port_state_with_mads(self, ib_port):
from tests.mlx5_prm_structs import IbSmp
in_mad = IbSmp(base_version=1, mgmt_class=IB_MGMT_CLASS_SUBN_LID_ROUTED,
class_version=1, method=IB_MGMT_METHOD_GET,
attr_id=IB_SMP_ATTR_PORT_INFO, attr_mod=ib_port)
ib_smp_out = IbSmp(self._send_mad_cmd(ib_port, in_mad, 0x3))
return ib_smp_out.data[32] & 0xf
def _send_mad_cmd(self, ib_port, in_mad, op_mod):
from tests.mlx5_prm_structs import MadIfcIn, MadIfcOut
mad_ifc_in = MadIfcIn(op_mod=op_mod, port=ib_port, mad=in_mad)
cmd_out = self.ctx.devx_general_cmd(mad_ifc_in, len(MadIfcOut()))
mad_ifc_out = MadIfcOut(cmd_out)
if mad_ifc_out.status:
raise PyverbsRDMAError(f'Failed to send MAD with syndrome ({mad_ifc_out.syndrome})')
return mad_ifc_out.mad
def check_cap_send_dbr_mode(self):
"""
Check the capability of the dbr less.
If the HCA cap have HCA cap 2, check if in HCA cap2 0x20(HCA CAP 2) + 0x1(current)
have the send_dbr_mode_no_dbr_ext.
"""
from tests.mlx5_prm_structs import QueryCmdHcaCap2Out, \
QueryHcaCapIn, QueryCmdHcaCapOut, QueryHcaCapOp, QueryHcaCapMod, SendDbrMode
self.create_context()
query_cap_in = QueryHcaCapIn(op_mod=0x1)
query_cap_out = QueryCmdHcaCapOut(self.ctx.devx_general_cmd(
query_cap_in, len(QueryCmdHcaCapOut())))
if query_cap_out.status:
raise PyverbsRDMAError('Failed to query general HCA CAPs with syndrome '
f'({query_cap_out.syndrome}')
if not query_cap_out.capability.hca_cap_2:
raise unittest.SkipTest("The device doesn't support general HCA CAPs 2")
query_cap2_in = QueryHcaCapIn(op_mod=(QueryHcaCapOp.HCA_CAP_2 << 0x1) | \
QueryHcaCapMod.CURRENT)
query_cap2_out = QueryCmdHcaCap2Out(self.ctx.devx_general_cmd(
query_cap2_in, len(QueryCmdHcaCap2Out())))
if self.send_dbr_mode == SendDbrMode.NO_DBR_EXT and \
not query_cap2_out.capability.send_dbr_mode_no_dbr_ext:
raise unittest.SkipTest("The device doesn't support send_dbr_mode_no_dbr_ext cap")
if self.send_dbr_mode == SendDbrMode.NO_DBR_INT and \
not query_cap2_out.capability.send_dbr_mode_no_dbr_int:
raise unittest.SkipTest("The device doesn't support send_dbr_mode_no_dbr_int cap")
def init_resources(self):
if not self.is_eth():
self.query_lid()
else:
is_gid_available(self.gid_index)
self.query_gid()
self.create_pd()
self.create_mr()
self.query_eqn()
self.create_uar()
self.create_queue_attrs()
self.create_eq()
self.create_cq()
self.create_qp()
# Objects closure order is important, and must be done manually in DevX
self.devx_objs = [self.qp, self.cq] + list(self.uar.values()) + list(self.umems.values()) + [self.msi_vector, self.eq]
def query_lid(self):
from tests.mlx5_prm_structs import QueryHcaVportContextIn, \
QueryHcaVportContextOut, QueryHcaCapIn, QueryCmdHcaCapOut
query_cap_in = QueryHcaCapIn(op_mod=0x1)
query_cap_out = QueryCmdHcaCapOut(self.ctx.devx_general_cmd(
query_cap_in, len(QueryCmdHcaCapOut())))
if query_cap_out.status:
raise PyverbsRDMAError('Failed to query general HCA CAPs with syndrome '
f'({query_cap_out.syndrome}')
port_num = self.ib_port if query_cap_out.capability.num_ports >= 2 else 0
query_port_in = QueryHcaVportContextIn(port_num=port_num)
query_port_out = QueryHcaVportContextOut(self.ctx.devx_general_cmd(
query_port_in, len(QueryHcaVportContextOut())))
if query_port_out.status:
raise PyverbsRDMAError('Failed to query vport with syndrome '
f'({query_port_out.syndrome})')
self.lid = query_port_out.hca_vport_context.lid
def query_gid(self):
gid = self.ctx.query_gid(self.ib_port, self.gid_index).gid.split(':')
for i in range(0, len(gid), 2):
self.gid[int(i/2)] = int(gid[i] + gid[i+1], 16)
def is_eth(self):
from tests.mlx5_prm_structs import QueryHcaCapIn, \
QueryCmdHcaCapOut
query_cap_in = QueryHcaCapIn(op_mod=0x1)
query_cap_out = QueryCmdHcaCapOut(self.ctx.devx_general_cmd(
query_cap_in, len(QueryCmdHcaCapOut())))
if query_cap_out.status:
raise PyverbsRDMAError('Failed to query general HCA CAPs with syndrome '
f'({query_cap_out.syndrome})')
return query_cap_out.capability.port_type # 0:IB, 1:ETH
@staticmethod
def roundup_pow_of_two(val):
return pow(2, math.ceil(math.log2(val)))
def create_queue_attrs(self):
# RQ calculations
wqe_size = WqeDataSeg.sizeof() * self.max_recv_sge
self.qattr.rq.wqe_size = self.roundup_pow_of_two(wqe_size)
max_recv_wr = self.roundup_pow_of_two(self.num_msgs)
self.qattr.rq.wq_size = max(self.qattr.rq.wqe_size * max_recv_wr,
MLX5_SEND_WQE_BB)
self.qattr.rq.wqe_num = math.ceil(self.qattr.rq.wq_size / self.qattr.rq.wqe_size)
self.qattr.rq.wqe_shift = int(math.log2(self.qattr.rq.wqe_size - 1)) + 1
# SQ calculations
self.qattr.sq.offset = self.qattr.rq.wq_size
# 192 = max overhead size of all structs needed for all operations in RC
wqe_size = 192 + WqeDataSeg.sizeof()
# Align wqe size to MLX5_SEND_WQE_BB
self.qattr.sq.wqe_size = (wqe_size + MLX5_SEND_WQE_BB - 1) & ~(MLX5_SEND_WQE_BB - 1)
self.qattr.sq.wq_size = self.roundup_pow_of_two(self.qattr.sq.wqe_size * self.num_msgs)
self.qattr.sq.wqe_num = math.ceil(self.qattr.sq.wq_size / MLX5_SEND_WQE_BB)
self.qattr.sq.wqe_shift = int(math.log2(MLX5_SEND_WQE_BB))
def create_context(self):
try:
attr = Mlx5DVContextAttr(mlx5dv_context_attr_flags.MLX5DV_CONTEXT_FLAGS_DEVX)
self.ctx = Mlx5Context(attr, self.dev_name)
except PyverbsUserError as ex:
raise unittest.SkipTest(f'Could not open mlx5 context ({ex})')
except PyverbsRDMAError:
raise unittest.SkipTest('Opening mlx5 DevX context is not supported')
def create_pd(self):
self.pd = PD(self.ctx)
self.dv_pd = Mlx5DvObj(mlx5dv_obj_type.MLX5DV_OBJ_PD, pd=self.pd).dvpd
def create_mr(self):
access = ibv_access_flags.IBV_ACCESS_REMOTE_WRITE | ibv_access_flags.IBV_ACCESS_LOCAL_WRITE | \
ibv_access_flags.IBV_ACCESS_REMOTE_READ
self.mr = MR(self.pd, self.msg_size, access)
def create_umem(self, size,
access=ibv_access_flags.IBV_ACCESS_LOCAL_WRITE,
alignment=resource.getpagesize()):
return Mlx5UMEM(self.ctx, size=size, alignment=alignment, access=access)
def create_uar(self):
self.uar['qp'] = Mlx5UAR(self.ctx, _MLX5DV_UAR_ALLOC_TYPE_NC)
self.uar['cq'] = Mlx5UAR(self.ctx, _MLX5DV_UAR_ALLOC_TYPE_NC)
if not self.uar['cq'].page_id or not self.uar['qp'].page_id:
raise PyverbsRDMAError('Failed to allocate UAR')
def query_eqn(self):
self.eqn = self.ctx.devx_query_eqn(0)
def create_cq(self):
from tests.mlx5_prm_structs import CreateCqIn, SwCqc, CreateCqOut
cq_size = self.roundup_pow_of_two(self.qattr.cq.cqe_size * self.qattr.cq.ncqes)
# Align to page size
pg_size = resource.getpagesize()
cq_size = (cq_size + pg_size - 1) & ~(pg_size - 1)
self.umems['cq'] = self.create_umem(size=cq_size)
self.umems['cq_dbr'] = self.create_umem(size=8, alignment=8)
log_cq_size = math.ceil(math.log2(self.qattr.cq.ncqes))
cmd_in = CreateCqIn(cq_umem_valid=1, cq_umem_id=self.umems['cq'].umem_id,
sw_cqc=SwCqc(c_eqn=self.eqn, uar_page=self.uar['cq'].page_id,
log_cq_size=log_cq_size, dbr_umem_valid=1,
dbr_umem_id=self.umems['cq_dbr'].umem_id))
self.cq = Mlx5DevxObj(self.ctx, cmd_in, len(CreateCqOut()))
def create_qp(self):
self.psn = random.getrandbits(24)
from tests.mlx5_prm_structs import SwQpc, CreateQpIn, DevxOps,\
CreateQpOut, CreateCqOut
self.psn = random.getrandbits(24)
qp_size = self.roundup_pow_of_two(self.qattr.rq.wq_size + self.qattr.sq.wq_size)
# Align to page size
pg_size = resource.getpagesize()
qp_size = (qp_size + pg_size - 1) & ~(pg_size - 1)
self.umems['qp'] = self.create_umem(size=qp_size)
self.umems['qp_dbr'] = self.create_umem(size=8, alignment=8)
self.log_rq_size = int(math.log2(self.qattr.rq.wqe_num - 1)) + 1
# Size of a receive WQE is 16*pow(2, log_rq_stride)
log_rq_stride = self.qattr.rq.wqe_shift - 4
log_sq_size = int(math.log2(self.qattr.sq.wqe_num - 1)) + 1
cqn = CreateCqOut(self.cq.out_view).cqn
qpc = SwQpc(st=DevxOps.MLX5_QPC_ST_RC, pd=self.dv_pd.pdn,
pm_state=DevxOps.MLX5_QPC_PM_STATE_MIGRATED,
log_rq_size=self.log_rq_size, log_sq_size=log_sq_size, ts_format=0x1,
log_rq_stride=log_rq_stride, uar_page=self.uar['qp'].page_id,
cqn_snd=cqn, cqn_rcv=cqn, dbr_umem_id=self.umems['qp_dbr'].umem_id,
dbr_umem_valid=1, send_dbr_mode=self.send_dbr_mode)
cmd_in = CreateQpIn(sw_qpc=qpc, wq_umem_id=self.umems['qp'].umem_id,
wq_umem_valid=1)
self.qp = Mlx5DevxObj(self.ctx, cmd_in, len(CreateQpOut()))
self.qpn = CreateQpOut(self.qp.out_view).qpn
def create_eq(self):
pass
def to_rts(self):
"""
Moves the created QP to RTS state by modifying it using DevX through all
the needed states with all the required attributes.
rlid, rpsn, rqpn and rgid (when valid) must be already updated before
calling this method.
"""
from tests.mlx5_prm_structs import DevxOps, ModifyQpIn, ModifyQpOut,\
CreateQpOut, SwQpc
cmd_out_len = len(ModifyQpOut())
# RST2INIT
qpn = CreateQpOut(self.qp.out_view).qpn
swqpc = SwQpc(rre=1, rwe=1)
swqpc.primary_address_path.vhca_port_num = self.ib_port
cmd_in = ModifyQpIn(opcode=DevxOps.MLX5_CMD_OP_RST2INIT_QP, qpn=qpn,
sw_qpc=swqpc)
self.qp.modify(cmd_in, cmd_out_len)
# INIT2RTR
swqpc = SwQpc(mtu=PATH_MTU, log_msg_max=20, remote_qpn=self.rqpn,
min_rnr_nak=MIN_RNR_TIMER, next_rcv_psn=self.rpsn)
swqpc.primary_address_path.vhca_port_num = self.ib_port
swqpc.primary_address_path.rlid = self.rlid
if self.is_eth():
# GID field is a must for Eth (or if GRH is set in IB)
swqpc.primary_address_path.rgid_rip = self.rgid
swqpc.primary_address_path.rmac = self.rmac
swqpc.primary_address_path.src_addr_index = self.gid_index
swqpc.primary_address_path.hop_limit = tests.utils.PacketConsts.TTL_HOP_LIMIT
# UDP sport must be reserved for roce v1 and v1.5
if self.ctx.query_gid_type(self.ib_port, self.gid_index) == ibv_gid_type_sysfs.IBV_GID_TYPE_SYSFS_ROCE_V2:
swqpc.primary_address_path.udp_sport = 0xdcba
else:
swqpc.primary_address_path.rlid = self.rlid
cmd_in = ModifyQpIn(opcode=DevxOps.MLX5_CMD_OP_INIT2RTR_QP, qpn=qpn,
sw_qpc=swqpc)
self.qp.modify(cmd_in, cmd_out_len)
# RTR2RTS
swqpc = SwQpc(retry_count=RETRY_CNT, rnr_retry=RNR_RETRY,
next_send_psn=self.psn, log_sra_max=MAX_RDMA_ATOMIC)
swqpc.primary_address_path.vhca_port_num = self.ib_port
swqpc.primary_address_path.ack_timeout = TIMEOUT
cmd_in = ModifyQpIn(opcode=DevxOps.MLX5_CMD_OP_RTR2RTS_QP, qpn=qpn,
sw_qpc=swqpc)
self.qp.modify(cmd_in, cmd_out_len)
def pre_run(self, rpsn, rqpn, rgid=0, rlid=0, rmac=0):
"""
Configure Resources before running traffic
:param rpsns: Remote PSN (packet serial number)
:param rqpn: Remote QP number
:param rgid: Remote GID
:param rlid: Remote LID
:param rmac: Remote MAC (valid for RoCE)
:return: None
"""
self.rpsn = rpsn
self.rqpn = rqpn
self.rgid = rgid
self.rlid = rlid
self.rmac = rmac
self.to_rts()
def post_send(self):
"""
Posts one send WQE to the SQ by doing all the required work such as
building the control/data segments, updating and ringing the dbr,
updating the producer indexes, etc.
"""
from tests.mlx5_prm_structs import SendDbrMode
buffer_address = self.uar['qp'].reg_addr
if self.send_dbr_mode == SendDbrMode.NO_DBR_EXT:
# Address of DB blueflame register
buffer_address = self.uar['qp'].base_addr + DB_BF_DBR_LESS_BUF_OFFSET
idx = self.qattr.sq.post_idx if self.qattr.sq.post_idx < self.qattr.sq.wqe_num else 0
buf_offset = self.qattr.sq.offset + (idx << MLX5_SEND_WQE_SHIFT)
# Prepare WQE
imm_be32 = struct.unpack("<I", struct.pack(">I", self.imm + self.qattr.sq.post_idx))[0]
ctrl_seg = WqeCtrlSeg(imm=imm_be32, fm_ce_se=MLX5_WQE_CTRL_CQ_UPDATE)
data_seg = self.get_wqe_data_segment()
ctrl_seg.opmod_idx_opcode = (self.qattr.sq.post_idx & 0xffff) << 8 | MLX5_OPCODE_SEND_IMM
size_in_octowords = int((ctrl_seg.sizeof() + data_seg.sizeof()) / 16)
ctrl_seg.qpn_ds = self.qpn << 8 | size_in_octowords
Wqe([ctrl_seg, data_seg], self.umems['qp'].umem_addr + buf_offset)
self.qattr.sq.post_idx += int((size_in_octowords * 16 +
MLX5_SEND_WQE_BB - 1) / MLX5_SEND_WQE_BB)
# Make sure descriptors are written
dma.udma_to_dev_barrier()
if not self.send_dbr_mode:
# Update the doorbell record
mem.writebe32(self.umems['qp_dbr'].umem_addr,
self.qattr.sq.post_idx & 0xffff, MLX5_SND_DBR)
dma.udma_to_dev_barrier()
# Ring the doorbell and post the WQE
dma.mmio_write64_as_be(buffer_address, mem.read64(ctrl_seg.addr))
def post_recv(self):
"""
Posts one receive WQE to the RQ by doing all the required work such as
building the control/data segments, updating the dbr and the producer
indexes.
"""
buf_offset = self.qattr.rq.offset + self.qattr.rq.wqe_size * self.qattr.rq.head
# Prepare WQE
data_seg = self.get_wqe_data_segment()
Wqe([data_seg], self.umems['qp'].umem_addr + buf_offset)
# Update indexes
self.qattr.rq.post_idx += 1
self.qattr.rq.head = self.qattr.rq.head + 1 if self.qattr.rq.head + 1 < self.qattr.rq.wqe_num else 0
# Update the doorbell record
dma.udma_to_dev_barrier()
mem.writebe32(self.umems['qp_dbr'].umem_addr,
self.qattr.rq.post_idx & 0xffff, MLX5_RCV_DBR)
def poll_cq(self):
"""
Polls the CQ once and updates the consumer index upon success.
The CQE opcode and owner bit are checked and verified.
This method does busy-waiting as long as it gets an empty CQE, until a
timeout of POLL_CQ_TIMEOUT seconds.
"""
idx = self.qattr.cq.cons_idx % self.qattr.cq.ncqes
cq_owner_flip = not(not(self.qattr.cq.cons_idx & self.qattr.cq.ncqes))
cqe_start_addr = self.umems['cq'].umem_addr + (idx * self.qattr.cq.cqe_size)
cqe = None
start_poll_t = time.perf_counter()
while cqe is None:
cqe = Mlx5Cqe64(cqe_start_addr)
if (cqe.opcode == MLX5_CQE_INVALID) or \
(cqe.owner ^ cq_owner_flip) or cqe.is_empty():
if time.perf_counter() - start_poll_t >= POLL_CQ_TIMEOUT:
raise PyverbsRDMAError(f'CQE #{self.qattr.cq.cons_idx} '
f'is empty or invalid:\n{cqe.dump()}')
cqe = None
# After CQE ownership check, must do memory barrier and re-read the CQE.
dma.udma_from_dev_barrier()
cqe = Mlx5Cqe64(cqe_start_addr)
if cqe.opcode == MLX5_CQE_RESP_ERR:
raise PyverbsRDMAError(f'Got a CQE #{self.qattr.cq.cons_idx} '
f'with responder error:\n{cqe.dump()}')
elif cqe.opcode == MLX5_CQE_REQ_ERR:
raise PyverbsRDMAError(f'Got a CQE #{self.qattr.cq.cons_idx} '
f'with requester error:\n{cqe.dump()}')
self.qattr.cq.cons_idx += 1
mem.writebe32(self.umems['cq_dbr'].umem_addr,
self.qattr.cq.cons_idx & 0xffffff, MLX5_CQ_SET_CI)
return cqe
def close_resources(self):
for obj in self.devx_objs:
if obj:
obj.close()
class Mlx5DevxTrafficBase(Mlx5RDMATestCase):
"""
A base class for mlx5 DevX traffic tests.
This class does not include any tests, but provides quick players (client,
server) creation and provides a traffic method.
"""
def tearDown(self):
if self.server:
self.server.close_resources()
if self.client:
self.client.close_resources()
super().tearDown()
def create_players(self, resources, **resource_arg):
"""
Initialize tests resources.
:param resources: The RDMA resources to use.
:param resource_arg: Dictionary of args that specify the resources
specific attributes.
:return: None
"""
self.server = resources(**self.dev_info, **resource_arg)
self.client = resources(**self.dev_info, **resource_arg)
self.pre_run()
def pre_run(self):
self.server.pre_run(self.client.psn, self.client.qpn, self.client.gid,
self.client.lid, self.mac_addr)
self.client.pre_run(self.server.psn, self.server.qpn, self.server.gid,
self.server.lid, self.mac_addr)
def invalidate_mr_pages(self):
if self.client.with_odp:
mem.madvise(self.client.mr.buf, self.client.msg_size)
self.client.mem_write('c' * self.client.msg_size, self.client.msg_size)
if self.server.with_odp:
mem.madvise(self.server.mr.buf, self.server.msg_size)
def send_imm_traffic(self):
self.client.mem_write('c' * self.client.msg_size, self.client.msg_size)
for _ in range(self.client.num_msgs):
cons_idx = self.client.qattr.cq.cons_idx
self.invalidate_mr_pages()
self.server.post_recv()
self.client.post_send()
# Poll client and verify received cqe opcode
send_cqe = self.client.poll_cq()
self.assertEqual(send_cqe.opcode, MLX5_CQE_REQ,
'Unexpected CQE opcode')
# Poll server and verify received cqe opcode
recv_cqe = self.server.poll_cq()
self.assertEqual(recv_cqe.opcode, MLX5_CQE_RESP_SEND_IMM,
'Unexpected CQE opcode')
msg_received = self.server.mem_read()
# Validate data (of received message and immediate value)
tests.utils.validate(msg_received, True, self.server.msg_size)
imm_inval_pkey = recv_cqe.imm_inval_pkey
if sys.byteorder == 'big':
imm_inval_pkey = int.from_bytes(
imm_inval_pkey.to_bytes(4, byteorder='big'), 'little')
self.assertEqual(imm_inval_pkey, self.client.imm + cons_idx)
self.server.mem_write('s' * self.server.msg_size, self.server.msg_size)
class Mlx5RcResources(RCResources):
def __init__(self, dev_name, ib_port, gid_index, **kwargs):
self.dv_send_ops_flags = 0
self.send_ops_flags = 0
self.create_send_ops_flags()
super().__init__(dev_name, ib_port, gid_index, **kwargs)
def create_send_ops_flags(self):
self.dv_send_ops_flags = 0
self.send_ops_flags = ibv_qp_create_send_ops_flags.IBV_QP_EX_WITH_SEND
def create_context(self):
mlx5dv_attr = Mlx5DVContextAttr()
try:
self.ctx = Mlx5Context(mlx5dv_attr, name=self.dev_name)
except PyverbsUserError as ex:
raise unittest.SkipTest(f'Could not open mlx5 context ({ex})')
except PyverbsRDMAError:
raise unittest.SkipTest('Opening mlx5 context is not supported')
def create_qp_init_attr(self):
comp_mask = ibv_qp_init_attr_mask.IBV_QP_INIT_ATTR_PD | ibv_qp_init_attr_mask.IBV_QP_INIT_ATTR_SEND_OPS_FLAGS
return QPInitAttrEx(cap=self.create_qp_cap(), pd=self.pd, scq=self.cq,
rcq=self.cq, qp_type=ibv_qp_type.IBV_QPT_RC,
send_ops_flags=self.send_ops_flags,
comp_mask=comp_mask)
def create_qps(self):
try:
qp_init_attr = self.create_qp_init_attr()
comp_mask = mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_QP_CREATE_FLAGS
if self.dv_send_ops_flags:
comp_mask |= mlx5dv_qp_init_attr_mask.MLX5DV_QP_INIT_ATTR_MASK_SEND_OPS_FLAGS
attr = Mlx5DVQPInitAttr(comp_mask=comp_mask,
send_ops_flags=self.dv_send_ops_flags)
qp = Mlx5QP(self.ctx, qp_init_attr, attr)
self.qps.append(qp)
self.qps_num.append(qp.qp_num)
self.psns.append(random.getrandbits(24))
except PyverbsRDMAError as ex:
if ex.error_code == errno.EOPNOTSUPP:
raise unittest.SkipTest('Create Mlx5DV QP is not supported')
raise ex
def create_cq(self):
"""
Initializes self.cq with a dv_cq
:return: None
"""
dvcq_init_attr = Mlx5DVCQInitAttr()
try:
self.cq = Mlx5CQ(self.ctx, CqInitAttrEx(), dvcq_init_attr)
except PyverbsRDMAError as ex:
if ex.error_code == errno.EOPNOTSUPP:
raise unittest.SkipTest('Create Mlx5DV CQ is not supported')
raise ex
def create_privileged_context(agr_obj):
"""
Creates a mlx5 privileged context.
:param agr_obj: Aggregation object which contains all resources necessary.
"""
fds_paths = ['/dev/infiniband/mlx5_perm_ctrl_local',
'/dev/infiniband/mlx5_perm_ctrl_other_vhca']
open_fds = []
try:
for file_path in fds_paths:
fd = open(file_path, 'rb')
open_fds.append(fd)
agr_obj.fds = FdArr(arr=[fd.fileno() for fd in open_fds], count=len(open_fds))
attr = Mlx5DVContextAttr(flags=mlx5dv_context_attr_flags.MLX5DV_CONTEXT_FLAGS_DEVX,
comp_mask=mlx5dv_context_attr_comp_mask.MLX5DV_CONTEXT_ATTR_MASK_FD_ARRAY,
fds=agr_obj.fds)
agr_obj.ctx = Mlx5Context(attr, agr_obj.dev_name)
for fd in open_fds: fd.close()
except FileNotFoundError:
for fd in open_fds: fd.close()
raise unittest.SkipTest(f'FDs file not found')
except PyverbsUserError as ex:
for fd in open_fds: fd.close()
raise unittest.SkipTest(f'Could not open mlx5 context ({ex})')
except PyverbsRDMAError as ex:
for fd in open_fds: fd.close()
raise unittest.SkipTest(f'Opening mlx5 DevX context is not supported ({ex})')
class Mlx5PrivilegedRC(Mlx5RcResources):
def __init__(self, dev_name, ib_port, gid_index, msg_size=1024, **kwargs):
"""
Initialize mlx5 DV privileged context resources based on Mlx5RcResources.
:param dev_name: Device name to be used
:param ib_port: IB port of the device to use
:param gid_index: Which GID index to use
:param msg_size: The resource msg size
:param kwargs: General arguments
"""
self.open_fds = []
super().__init__(dev_name, ib_port, gid_index, msg_size=msg_size, **kwargs)
def create_context(self):
return create_privileged_context(self)
def close_fds(self):
for open_fd in self.open_fds:
open_fd.close()
|