def poll_cq(cq, count=1, data=None): """ Poll <count> completions from the CQ. Note: This function calls the blocking poll() method of the CQ until <count> completions were received. Alternatively, gets a single CQ event when events are used. :param cq: CQ to poll from :param count: How many completions to poll :param data: In case of a work request with immediate, the immediate data to be compared after poll :return: An array of work completions of length <count>, None when events are used """ wcs = [] channel = cq.comp_channel while count > 0: if channel: channel.get_cq_event(cq) cq.req_notify() nc, tmp_wcs = cq.poll(count) for wc in tmp_wcs: if wc.status != e.IBV_WC_SUCCESS: raise PyverbsRDMAError( 'Completion status is {s}'.format( s=wc_status_to_str(wc.status)), wc.status) if data: if wc.wc_flags & e.IBV_WC_WITH_IMM == 0: raise PyverbsRDMAError('Completion without immediate') assert socket.ntohl(wc.imm_data) == data count -= nc wcs.extend(tmp_wcs) return wcs
def poll_cq_ex_ts(cqex, ts_type=None): """ Poll completion from the extended CQ. :param cqex: CQEX to poll from :param ts_type: If set, read the CQE timestamp in this format :return: The CQE timestamp if it requested. """ polling_timeout = 10 start = datetime.datetime.now() ts = 0 poll_attr = PollCqAttr() ret = cqex.start_poll(poll_attr) while ret == 2 and (datetime.datetime.now() - start).seconds < polling_timeout: ret = cqex.start_poll(poll_attr) if ret == 2: raise PyverbsRDMAError('Failed to poll CQEX - Got timeout') if ret != 0: raise PyverbsRDMAError('Failed to poll CQEX') if cqex.status != e.IBV_WC_SUCCESS: raise PyverbsRDMAError('Completion status is {cqex.status}') if ts_type == FREE_RUNNING: ts = cqex.read_timestamp() if ts_type == REAL_TIME: ts = cqex.read_completion_wallclock_ns() cqex.end_poll() return ts
def test_query_gid_ex_bad_flow(self): """ Test ibv_query_gid_ex() with an empty index """ ctx, device_attr, _ = self.devices[0] try: port_attr = ctx.query_port(1) max_entries = device_attr.phys_port_cnt * port_attr.gid_tbl_len gid_indices = { gid_entry.gid_index for gid_entry in ctx.query_gid_table(max_entries) if gid_entry.port_num == 1 } possible_indices = set(range(port_attr.gid_tbl_len)) try: no_gid_index = possible_indices.difference(gid_indices).pop() except KeyError: # all indices are populated by GIDs raise unittest.SkipTest('All gid indices populated,' ' cannot check bad flow') ctx.query_gid_ex(port_num=1, gid_index=no_gid_index) except PyverbsRDMAError as ex: if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]: raise unittest.SkipTest('ibv_query_gid_ex is not' ' supported on this device') self.assertEqual( ex.error_code, errno.ENODATA, f'Got {os.strerror(ex.error_code)} but ' f'Expected {os.strerror(errno.ENODATA)}') else: raise PyverbsRDMAError(f'Successfully queried ' 'non-existent gid index {i}')
def func_wrapper(instance): try: ctx = Mlx5Context(Mlx5DVContextAttr(), instance.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') # Query NIC Flow Table capabilities cmd_in = struct.pack('!HIH8s', MLX5_CMD_OP_QUERY_HCA_CAP, 0, MLX5_CMD_MOD_NIC_FLOW_TABLE_CAP << 1 | 0x1, bytes(8)) try: cmd_out = Mlx5Context.devx_general_cmd( ctx, cmd_in, MLX5_CMD_OP_QUERY_HCA_CAP_OUT_LEN) except PyverbsRDMAError as ex: if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]: raise unittest.SkipTest( 'DevX general command is not supported') raise ex cmd_view = memoryview(cmd_out) status = cmd_view[0] if status: raise PyverbsRDMAError( 'Query NIC Flow Table CAPs failed with status' f' ({status})') # Verify that both NIC RX and TX support reformat actions by checking # the following PRM fields: encap_general_header, # log_max_packet_reformat, and reformat (for both RX and TX). if not (cmd_view[20] & 0x80 and cmd_view[21] & 0x1f and cmd_view[80] & 0x1 and cmd_view[272] & 0x1): raise unittest.SkipTest('NIC flow table does not support reformat') return func(instance)
def test_dmabuf_reg_mr_bad_flags(self): """ Verify that illegal flags combination fails as expected """ check_dmabuf_support(self.gpu) for ctx, attr, attr_ex in self.devices: with PD(ctx) as pd: check_dmabuf_mr_support(pd, self.gpu) for i in range(5): flags = random.sample([ e.IBV_ACCESS_REMOTE_WRITE, e.IBV_ACCESS_REMOTE_ATOMIC ], random.randint(1, 2)) mr_flags = 0 for i in flags: mr_flags += i.value try: DmaBufMR(pd, u.get_mr_length(), mr_flags, gpu=self.gpu, gtt=self.gtt) except PyverbsRDMAError as err: assert 'Failed to register a dma-buf MR' in err.args[0] else: raise PyverbsRDMAError( 'Registered a dma-buf MR with illegal falgs')
def test_query_gid_ex_bad_flow(self): """ Test ibv_query_gid_ex() with an empty index """ try: port_attr = self.ctx.query_port(self.ib_port) max_entries = 0 for port_num in range(1, self.attr.phys_port_cnt + 1): attr = self.ctx.query_port(port_num) max_entries += attr.gid_tbl_len if max_entries > 0: gid_indices = {gid_entry.gid_index for gid_entry in self.ctx.query_gid_table(max_entries) if gid_entry.port_num == self.ib_port} else: gid_indices = {} possible_indices = set(range(port_attr.gid_tbl_len)) if port_attr.gid_tbl_len > 1 else set() try: no_gid_index = possible_indices.difference(gid_indices).pop() except KeyError: # all indices are populated by GIDs raise unittest.SkipTest('All gid indices populated,' ' cannot check bad flow') self.ctx.query_gid_ex(port_num=self.ib_port, gid_index=no_gid_index) except PyverbsRDMAError as ex: if ex.error_code in [errno.EOPNOTSUPP, errno.EPROTONOSUPPORT]: raise unittest.SkipTest('ibv_query_gid_ex is not' ' supported on this device') self.assertEqual(ex.error_code, errno.ENODATA, f'Got {os.strerror(ex.error_code)} but ' f'Expected {os.strerror(errno.ENODATA)}') else: raise PyverbsRDMAError('Successfully queried ' f'non-existent gid index {no_gid_index}')
def poll_cq(cq, count=1): """ Poll <count> completions from the CQ. Note: This function calls the blocking poll() method of the CQ until <count> completions were received. Alternatively, gets a single CQ event when events are used. :param cq: CQ to poll from :param count: How many completions to poll :return: An array of work completions of length <count>, None when events are used """ wcs = [] channel = cq.comp_channel while count > 0: if channel: channel.get_cq_event(cq) cq.req_notify() nc, tmp_wcs = cq.poll(count) for wc in tmp_wcs: if wc.status != e.IBV_WC_SUCCESS: raise PyverbsRDMAError('Completion status is {s}'.format( s=wc_status_to_str(wc.status))) count -= nc wcs.extend(tmp_wcs) return wcs
def wrapper(instance): cmd_in = struct.pack('!HIH8s', MLX5_CMD_OP_QUERY_HCA_CAP, 0, MLX5_CMD_MOD_DEVICE_MEMORY_CAP << 1 | 0x1, bytes(8)) cmd_out = Mlx5Context.devx_general_cmd( instance.ctx, cmd_in, MLX5_CMD_OP_QUERY_HCA_CAP_OUT_LEN) cmd_view = memoryview(cmd_out) status = cmd_view[0] if status: raise PyverbsRDMAError( 'Query Device Memory CAPs failed with status' f' ({status})') memic_op_support = int.from_bytes(cmd_view[80:84], 'big') increment_size_sup = cmd_view[20] test_and_set_size_sup = cmd_view[22] # Verify that MEMIC atomic operations (both increment and test_and_set) # are supported with write/read size of 1 Byte. if memic_op_support & 0x3 != 0x3: raise unittest.SkipTest( 'MEMIC atomic operations are not supported') if not increment_size_sup & test_and_set_size_sup & 0x1: raise unittest.SkipTest( 'MEMIC atomic operations are not supported with 1 Bytes read/write sizes' ) return func(instance)
def test_mlx5vfio_rc_qp_send_imm_traffic(self): """ Opens one mlx5 vfio context, creates two DevX RC QPs on it, and modifies them to RTS state. Then does SEND_IMM traffic. """ self.create_players() if self.server.is_eth(): raise unittest.SkipTest( f'{self.__class__.__name__} is currently supported over IB only' ) self.event_ex = [] self.proc_events = True proc_events = Thread(target=self.vfio_processs_events) proc_events.start() # Move the DevX QPs ro RTS state self.pre_run() try: # Send traffic self.send_imm_traffic() finally: # Stop listening to events self.proc_events = False proc_events.join() if self.event_ex: raise PyverbsRDMAError( f'Received unexpected vfio events: {self.event_ex}')
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 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 check_mkey(self, player, expected=dve.MLX5DV_MKEY_NO_ERR): """ Check the player's mkey for a signature error. param player: Player to check. param expected: The expected result of the checking. """ mkey_err = player.mkey.mkey_check() if mkey_err.err_type != expected: raise PyverbsRDMAError('MKEY check failed: ' f'expected err_type: {expected_type}, ' f'actual err_type: {mkey_err.err_type}')
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
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 test_mlx5vfio_async_event(self): """ Opens one mlx5 vfio context, creates DevX EQ on it. Then activates the port and catches the port state change event. """ self.create_async_players() if self.server.is_eth(): raise unittest.SkipTest(f'{self.__class__.__name__} is currently supported over IB only') self.event_ex = [] self.proc_events = True self.caught_event = False proc_events = Thread(target=self.vfio_process_events) proc_async_events = Thread(target=self.vfio_process_async_events) proc_events.start() proc_async_events.start() # Move the DevX QPs to RTS state self.pre_run() try: # Change port state self.server.change_port_state_with_registers(PortStatus.MLX5_PORT_UP) admin_status, oper_status = self.server.query_port_state_with_registers() start_state_t = time.perf_counter() 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') admin_status, oper_status = self.server.query_port_state_with_registers() start_state_t = time.perf_counter() while self.server.query_port_state_with_mads(self.ib_port) < PortState.ACTIVE: if time.perf_counter() - start_state_t >= PORT_STATE_TIMEOUT: raise PyverbsRDMAError('Could not change the port state to ACTIVE') time.sleep(1) finally: # Stop listening to events self.proc_events = False proc_events.join() proc_async_events.join() if self.event_ex: raise PyverbsRDMAError(f'Received unexpected vfio events: {self.event_ex}') if not self.caught_event: raise PyverbsRDMAError('Failed to catch an async event')
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 == dve.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 == dve.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 == dve.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 get_device_list(self): lst = d.get_device_list() if len(lst) == 0: raise unittest.SkipTest('No IB device found') dev_name = self.config['dev'] if dev_name: for dev in lst: if dev.name.decode() == dev_name: lst = [dev] break if len(lst) == 0: raise PyverbsRDMAError(f'No IB device with name {dev_name} found') return lst
def vfio_process_events(self): """ Processes mlx5 vfio device events. This method should run from application thread to maintain the events. """ # Server and client use the same context events_fd = self.server.ctx.get_events_fd() with select.epoll() as epoll_events: epoll_events.register(events_fd, select.EPOLLIN) while self.proc_events: for fd, event in epoll_events.poll(timeout=0.1): if fd == events_fd: if not (event & select.EPOLLIN): self.event_ex.append(PyverbsRDMAError(f'Unexpected vfio event: {event}')) self.server.ctx.process_events()
def test_query_port_bad_flow(self): """ Verify that querying non-existing ports fails as expected """ lst = d.get_device_list() for dev in lst: with d.Context(name=dev.name.decode()) as ctx: num_ports = ctx.query_device().phys_port_cnt try: port = num_ports + random.randint(1, 10) ctx.query_port(port) except PyverbsRDMAError as e: assert 'Failed to query port' in e.args[0] assert 'Invalid argument' in e.args[0] else: raise PyverbsRDMAError('Successfully queried non-existing port {p}'.\ format(p=port))
def test_query_gid_table_bad_flow(self): """ Test ibv_query_gid_table() with too small a buffer """ try: self.ctx.query_gid_table(0) except PyverbsRDMAError as ex: if ex.error_code in [-errno.EOPNOTSUPP, -errno.EPROTONOSUPPORT]: raise unittest.SkipTest('ibv_query_gid_table is not' ' supported on this device') self.assertEqual(ex.error_code, -errno.EINVAL, f'Got -{os.strerror(-ex.error_code)} but ' f'Expected -{os.strerror(errno.EINVAL)} ') else: raise PyverbsRDMAError('Successfully queried ' 'gid_table with an insufficient buffer')
def process_async_events(self, fd): from tests.mlx5_prm_structs import EventType cc = 0 ret = os.read(fd, 8) if not ret: raise PyverbsRDMAErrno('Failed to read FD') eqe = self.get_eqe(cc) while eqe: if eqe.event_type == EventType.PORT_STATE_CHANGE: self.logger.debug('Caught port state change event') return eqe.event_type elif eqe.event_type == EventType.CQ_ERROR: raise PyverbsRDMAError('Event type Error') cc = self.update_cc(cc + 1) eqe = self.get_eqe(cc) self.update_ci(cc, 1)
def vfio_process_async_events(self): """ Processes mlx5 vfio device async events. This method should run from application thread to maintain the events. """ from tests.mlx5_prm_structs import EventType # Server and client use the same context events_fd = self.server.msi_vector.fd with select.epoll() as epoll_events: epoll_events.register(events_fd, select.EPOLLIN) while self.proc_events: for fd, event in epoll_events.poll(timeout=0.1): if fd == events_fd: if not (event & select.EPOLLIN): self.event_ex.append(PyverbsRDMAError(f'Unexpected vfio event: {event}')) if self.server.process_async_events(events_fd) == EventType.PORT_STATE_CHANGE: self.caught_event = True
def test_reg_mr_bad_flags(self): """ Verify that illegal flags combination fails as expected """ lst = d.get_device_list() for dev in lst: with d.Context(name=dev.name.decode()) as ctx: with PD(ctx) as pd: flags = random.sample([ e.IBV_ACCESS_REMOTE_WRITE, e.IBV_ACCESS_REMOTE_ATOMIC ], random.randint(1, 2)) mr_flags = 0 for i in flags: mr_flags += i.value try: mr = MR(pd, u.get_mr_length(), mr_flags) except PyverbsRDMAError as err: assert 'Failed to register a MR' in err.args[0] else: raise PyverbsRDMAError( 'Registered a MR with illegal falgs')
def func_wrapper(instance): try: ctx = Mlx5Context(Mlx5DVContextAttr(), instance.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') # Query NIC Flow Table capabilities cmd_in = struct.pack('!HIH8s', MLX5_CMD_OP_QUERY_HCA_CAP, 0, MLX5_CMD_MOD_NIC_FLOW_TABLE_CAP << 1 | 0x1, bytes(8)) cmd_out = Mlx5Context.devx_general_cmd( ctx, cmd_in, MLX5_CMD_OP_QUERY_HCA_CAP_OUT_LEN) cmd_view = memoryview(cmd_out) status = cmd_view[0] if status: raise PyverbsRDMAError( 'Query NIC Flow Table CAPs failed with status' f' ({status})') # Verify that both NIC RX and TX support reformat actions if not (cmd_view[80] & 0x1 and cmd_view[272] & 0x1): raise unittest.SkipTest('NIC flow table does not support reformat') return func(instance)
def create_uar(self): super().create_uar() self.uar['eq'] = Mlx5UAR(self.ctx, dve._MLX5DV_UAR_ALLOC_TYPE_NC) if not self.uar['eq'].page_id: raise PyverbsRDMAError('Failed to allocate UAR')