| # SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) |
| # Copyright (c) 2019 Mellanox Technologies, Inc. All rights reserved. See COPYING file |
| |
| from libc.stdint cimport uintptr_t, uint8_t, uint16_t, uint32_t |
| from libc.string cimport memcpy, memset |
| from libc.stdlib cimport calloc, free |
| from posix.mman cimport munmap |
| import logging |
| import weakref |
| |
| from pyverbs.providers.mlx5.mlx5dv_mkey cimport Mlx5MrInterleaved, Mlx5Mkey, \ |
| Mlx5MkeyConfAttr, Mlx5SigBlockAttr |
| from pyverbs.providers.mlx5.mlx5dv_crypto cimport Mlx5CryptoLoginAttr, Mlx5CryptoAttr |
| from pyverbs.pyverbs_error import PyverbsUserError, PyverbsRDMAError, PyverbsError |
| from pyverbs.providers.mlx5.dr_action cimport DrActionFlowCounter, DrActionDestTir |
| from pyverbs.providers.mlx5.mlx5dv_sched cimport Mlx5dvSchedLeaf |
| cimport pyverbs.providers.mlx5.mlx5dv_enums as dve |
| cimport pyverbs.providers.mlx5.libmlx5 as dv |
| from pyverbs.mem_alloc import posix_memalign |
| from pyverbs.qp cimport QPInitAttrEx, QPEx |
| from pyverbs.base import PyverbsRDMAErrno |
| from pyverbs.base cimport close_weakrefs |
| from pyverbs.wr cimport copy_sg_array |
| cimport pyverbs.libibverbs_enums as e |
| from pyverbs.cq cimport CqInitAttrEx |
| cimport pyverbs.libibverbs as v |
| from pyverbs.device cimport DM |
| from pyverbs.addr cimport AH |
| from pyverbs.pd cimport PD |
| |
| |
| cdef extern from 'endian.h': |
| unsigned long htobe16(unsigned long host_16bits) |
| unsigned long be16toh(unsigned long network_16bits) |
| unsigned long htobe32(unsigned long host_32bits) |
| unsigned long be32toh(unsigned long network_32bits) |
| unsigned long htobe64(unsigned long host_64bits) |
| unsigned long be64toh(unsigned long network_64bits) |
| |
| |
| cdef char* _prepare_devx_inbox(in_bytes): |
| """ |
| Auxiliary function that allocates inboxes for DevX commands, and fills them |
| the bytes input. |
| The allocated box must be freed when it's no longer needed. |
| :param in_bytes: Stream of bytes of the command's input |
| :return: The C allocated inbox |
| """ |
| cdef char *in_bytes_c = in_bytes |
| cdef char* in_mailbox = <char*>calloc(1, len(in_bytes)) |
| if in_mailbox == NULL: |
| raise MemoryError('Failed to allocate memory') |
| memcpy(in_mailbox, in_bytes_c, len(in_bytes)) |
| return in_mailbox |
| |
| |
| cdef char* _prepare_devx_outbox(outlen): |
| """ |
| Auxiliary function that allocates the outboxes for DevX commands. |
| The allocated box must be freed when it's no longer needed. |
| :param outlen: Output command's length in bytes |
| :return: The C allocated outbox |
| """ |
| cdef char* out_mailbox = <char*>calloc(1, outlen) |
| if out_mailbox == NULL: |
| raise MemoryError('Failed to allocate memory') |
| return out_mailbox |
| |
| |
| cdef uintptr_t copy_data_to_addr(uintptr_t addr, data): |
| """ |
| Auxiliary function that copies data to memory at provided address. |
| :param addr: Address to copy the data to |
| :param data: Data to copy |
| :return: The incremented address to the end of the written data |
| """ |
| cdef bytes py_bytes = bytes(data) |
| cdef char *tmp = py_bytes |
| memcpy(<void *>addr, tmp, len(data)) |
| return addr + len(data) |
| |
| |
| cdef class Mlx5DVPortAttr(PyverbsObject): |
| """ |
| Represents mlx5dv_port struct, which exposes mlx5-specific capabilities, |
| reported by mlx5dv_query_port() |
| """ |
| def __init__(self): |
| super().__init__() |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('flags', hex(self.attr.flags)) |
| |
| @property |
| def flags(self): |
| return self.attr.flags |
| |
| @property |
| def vport(self): |
| return self.attr.vport |
| |
| @property |
| def vport_vhca_id(self): |
| return self.attr.vport_vhca_id |
| |
| @property |
| def esw_owner_vhca_id(self): |
| return self.attr.esw_owner_vhca_id |
| |
| @property |
| def vport_steering_icm_rx(self): |
| return self.attr.vport_steering_icm_rx |
| |
| @property |
| def vport_steering_icm_tx(self): |
| return self.attr.vport_steering_icm_tx |
| |
| @property |
| def reg_c0_value(self): |
| return self.attr.reg_c0.value |
| |
| @property |
| def reg_c0_mask(self): |
| return self.attr.reg_c0.mask |
| |
| |
| cdef class Mlx5DVContextAttr(PyverbsObject): |
| """ |
| Represent mlx5dv_context_attr struct. This class is used to open an mlx5 |
| device. |
| """ |
| def __init__(self, flags=0, comp_mask=0): |
| super().__init__() |
| self.attr.flags = flags |
| self.attr.comp_mask = comp_mask |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('flags', self.attr.flags) +\ |
| print_format.format('comp_mask', self.attr.comp_mask) |
| |
| @property |
| def flags(self): |
| return self.attr.flags |
| @flags.setter |
| def flags(self, val): |
| self.attr.flags = val |
| |
| @property |
| def comp_mask(self): |
| return self.attr.comp_mask |
| @comp_mask.setter |
| def comp_mask(self, val): |
| self.attr.comp_mask = val |
| |
| |
| cdef class Mlx5DevxObj(PyverbsCM): |
| """ |
| Represents mlx5dv_devx_obj C struct. |
| """ |
| def __init__(self, Context context, in_, outlen): |
| """ |
| Creates a DevX object. |
| If the object was successfully created, the command's output would be |
| stored as a memoryview in self.out_view. |
| :param in_: Bytes of the obj_create command's input data provided in a |
| device specification format. |
| (Stream of bytes or __bytes__ is implemented) |
| :param outlen: Expected output length in bytes |
| """ |
| super().__init__() |
| in_bytes = bytes(in_) |
| cdef char *in_mailbox = _prepare_devx_inbox(in_bytes) |
| cdef char *out_mailbox = _prepare_devx_outbox(outlen) |
| self.obj = dv.mlx5dv_devx_obj_create(context.context, in_mailbox, |
| len(in_bytes), out_mailbox, outlen) |
| try: |
| if self.obj == NULL: |
| raise PyverbsRDMAErrno('Failed to create DevX object') |
| self.out_view = memoryview(out_mailbox[:outlen]) |
| status = hex(self.out_view[0]) |
| syndrome = self.out_view[4:8].hex() |
| if status != hex(0): |
| raise PyverbsRDMAError('Failed to create DevX object with status' |
| f'({status}) and syndrome (0x{syndrome})') |
| finally: |
| free(in_mailbox) |
| free(out_mailbox) |
| self.context = context |
| self.context.add_ref(self) |
| self.flow_counter_actions = weakref.WeakSet() |
| self.dest_tir_actions = weakref.WeakSet() |
| |
| def query(self, in_, outlen): |
| """ |
| Queries the DevX object. |
| :param in_: Bytes of the obj_query command's input data provided in a |
| device specification format. |
| (Stream of bytes or __bytes__ is implemented) |
| :param outlen: Expected output length in bytes |
| :return: Bytes of the command's output |
| """ |
| in_bytes = bytes(in_) |
| cdef char *in_mailbox = _prepare_devx_inbox(in_bytes) |
| cdef char *out_mailbox = _prepare_devx_outbox(outlen) |
| rc = dv.mlx5dv_devx_obj_query(self.obj, in_mailbox, len(in_bytes), |
| out_mailbox, outlen) |
| try: |
| if rc: |
| raise PyverbsRDMAError('Failed to query DevX object', rc) |
| out = <bytes>out_mailbox[:outlen] |
| finally: |
| free(in_mailbox) |
| free(out_mailbox) |
| return out |
| |
| def modify(self, in_, outlen): |
| """ |
| Modifies the DevX object. |
| :param in_: Bytes of the obj_modify command's input data provided in a |
| device specification format. |
| (Stream of bytes or __bytes__ is implemented) |
| :param outlen: Expected output length in bytes |
| :return: Bytes of the command's output |
| """ |
| in_bytes = bytes(in_) |
| cdef char *in_mailbox = _prepare_devx_inbox(in_bytes) |
| cdef char *out_mailbox = _prepare_devx_outbox(outlen) |
| rc = dv.mlx5dv_devx_obj_modify(self.obj, in_mailbox, len(in_bytes), |
| out_mailbox, outlen) |
| try: |
| if rc: |
| raise PyverbsRDMAError('Failed to modify DevX object', rc) |
| out = <bytes>out_mailbox[:outlen] |
| finally: |
| free(in_mailbox) |
| free(out_mailbox) |
| return out |
| |
| cdef add_ref(self, obj): |
| if isinstance(obj, DrActionFlowCounter): |
| self.flow_counter_actions.add(obj) |
| elif isinstance(obj, DrActionDestTir): |
| self.dest_tir_actions.add(obj) |
| else: |
| raise PyverbsError('Unrecognized object type') |
| |
| @property |
| def out_view(self): |
| return self.out_view |
| |
| @property |
| def obj(self): |
| return <object>self.obj |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.obj != NULL: |
| if self.logger: |
| self.logger.debug('Closing Mlx5DvexObj') |
| close_weakrefs([self.flow_counter_actions, self.dest_tir_actions]) |
| rc = dv.mlx5dv_devx_obj_destroy(self.obj) |
| if rc: |
| raise PyverbsRDMAError('Failed to destroy a DevX object', rc) |
| self.obj = NULL |
| self.context = None |
| |
| |
| cdef class Mlx5Context(Context): |
| """ |
| Represent mlx5 context, which extends Context. |
| """ |
| def __init__(self, Mlx5DVContextAttr attr not None, name=''): |
| """ |
| Open an mlx5 device using the given attributes |
| :param name: The RDMA device's name (used by parent class) |
| :param attr: mlx5-specific device attributes |
| :return: None |
| """ |
| super().__init__(name=name, attr=attr) |
| if not dv.mlx5dv_is_supported(self.device): |
| raise PyverbsUserError('This is not an MLX5 device') |
| self.context = dv.mlx5dv_open_device(self.device, &attr.attr) |
| if self.context == NULL: |
| raise PyverbsRDMAErrno('Failed to open mlx5 context on {dev}' |
| .format(dev=self.name)) |
| self.devx_umems = weakref.WeakSet() |
| self.devx_objs = weakref.WeakSet() |
| self.devx_eqs = weakref.WeakSet() |
| |
| def query_mlx5_device(self, comp_mask=-1): |
| """ |
| Queries the provider for device-specific attributes. |
| :param comp_mask: Which attributes to query. Default value is -1. If |
| not changed by user, pyverbs will pass a bitwise OR |
| of all available enum entries. |
| :return: A Mlx5DVContext containing the attributes. |
| """ |
| dv_attr = Mlx5DVContext() |
| if comp_mask == -1: |
| dv_attr.comp_mask = \ |
| dve.MLX5DV_CONTEXT_MASK_CQE_COMPRESION |\ |
| dve.MLX5DV_CONTEXT_MASK_SWP |\ |
| dve.MLX5DV_CONTEXT_MASK_STRIDING_RQ |\ |
| dve.MLX5DV_CONTEXT_MASK_TUNNEL_OFFLOADS |\ |
| dve.MLX5DV_CONTEXT_MASK_DYN_BFREGS |\ |
| dve.MLX5DV_CONTEXT_MASK_CLOCK_INFO_UPDATE |\ |
| dve.MLX5DV_CONTEXT_MASK_DC_ODP_CAPS |\ |
| dve.MLX5DV_CONTEXT_MASK_FLOW_ACTION_FLAGS |\ |
| dve.MLX5DV_CONTEXT_MASK_DCI_STREAMS |\ |
| dve.MLX5DV_CONTEXT_MASK_WR_MEMCPY_LENGTH |\ |
| dve.MLX5DV_CONTEXT_MASK_CRYPTO_OFFLOAD |\ |
| dve.MLX5DV_CONTEXT_MASK_MAX_DC_RD_ATOM |
| else: |
| dv_attr.comp_mask = comp_mask |
| rc = dv.mlx5dv_query_device(self.context, &dv_attr.dv) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to query mlx5 device {self.name}.', rc) |
| return dv_attr |
| |
| @staticmethod |
| def query_mlx5_port(Context ctx, port_num): |
| dv_attr = Mlx5DVPortAttr() |
| rc = dv.mlx5dv_query_port(ctx.context, port_num, &dv_attr.attr) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to query dv port mlx5 {ctx.name} port {port_num}.', rc) |
| return dv_attr |
| |
| @staticmethod |
| def reserved_qpn_alloc(Context ctx): |
| """ |
| Allocate a reserved QP number from firmware. |
| :param ctx: The device context to issue the action on. |
| :return: The reserved QP number. |
| """ |
| cdef uint32_t qpn |
| rc = dv.mlx5dv_reserved_qpn_alloc(ctx.context, &qpn) |
| if rc != 0: |
| raise PyverbsRDMAError('Failed to alloc reserved QP number.', rc) |
| return qpn |
| |
| @staticmethod |
| def reserved_qpn_dealloc(Context ctx, qpn): |
| """ |
| Release the reserved QP number to firmware. |
| :param ctx: The device context to issue the action on. |
| :param qpn: The QP number to be deallocated. |
| """ |
| rc = dv.mlx5dv_reserved_qpn_dealloc(ctx.context, qpn) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to dealloc QP number {qpn}.', rc) |
| |
| @staticmethod |
| def crypto_login(Context ctx, Mlx5CryptoLoginAttr login_attr): |
| """ |
| Creates a crypto login session |
| :param ctx: The device context to issue the action on. |
| :param login_attr: Mlx5CryptoLoginAttr object which contains the |
| credential to login with and the import KEK to be |
| used for secured communications. |
| """ |
| rc = dv.mlx5dv_crypto_login(ctx.context, &login_attr.mlx5dv_crypto_login_attr) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to create crypto login session.', rc) |
| |
| @staticmethod |
| def query_login_state(Context ctx): |
| """ |
| Queries the state of the current crypto login session. |
| :param ctx: The device context to issue the action on. |
| :return: The login state. |
| """ |
| cdef dv.mlx5dv_crypto_login_state state |
| rc = dv.mlx5dv_crypto_login_query_state(ctx.context, &state) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to query the crypto login session state.', rc) |
| return state |
| |
| @staticmethod |
| def crypto_logout(Context ctx): |
| """ |
| Logs out from the current crypto login session. |
| :param ctx: The device context to issue the action on. |
| """ |
| rc = dv.mlx5dv_crypto_logout(ctx.context) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to logout from crypto login session.', rc) |
| |
| def devx_general_cmd(self, in_, outlen): |
| """ |
| Executes a DevX general command according to the input mailbox. |
| :param in_: Bytes of the general command's input data provided in a |
| device specification format. |
| (Stream of bytes or __bytes__ is implemented) |
| :param outlen: Expected output length in bytes |
| :return out: Bytes of the general command's output data provided in a |
| device specification format |
| """ |
| in_bytes = bytes(in_) |
| cdef char *in_mailbox = _prepare_devx_inbox(in_bytes) |
| cdef char *out_mailbox = _prepare_devx_outbox(outlen) |
| rc = dv.mlx5dv_devx_general_cmd(self.context, in_mailbox, len(in_bytes), |
| out_mailbox, outlen) |
| try: |
| if rc: |
| raise PyverbsRDMAError("DevX general command failed", rc) |
| out = <bytes>out_mailbox[:outlen] |
| finally: |
| free(in_mailbox) |
| free(out_mailbox) |
| return out |
| |
| @staticmethod |
| def device_timestamp_to_ns(Context ctx, device_timestamp): |
| """ |
| Convert device timestamp from HCA core clock units to the corresponding |
| nanosecond units. The function uses mlx5dv_get_clock_info to get the |
| device clock information. |
| :param ctx: The device context to issue the action on. |
| :param device_timestamp: The device timestamp to convert. |
| :return: Timestamp in nanoseconds |
| """ |
| cdef dv.mlx5dv_clock_info *clock_info |
| clock_info = <dv.mlx5dv_clock_info *>calloc(1, sizeof(dv.mlx5dv_clock_info)) |
| rc = dv.mlx5dv_get_clock_info(ctx.context, clock_info) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to get the clock info', rc) |
| |
| ns_time = dv.mlx5dv_ts_to_ns(clock_info, device_timestamp) |
| free(clock_info) |
| return ns_time |
| |
| def devx_query_eqn(self, vector): |
| """ |
| Query EQN for a given vector id. |
| :param vector: Completion vector number |
| :return: The device EQ number which relates to the given input vector |
| """ |
| cdef uint32_t eqn |
| rc = dv.mlx5dv_devx_query_eqn(self.context, vector, &eqn) |
| if rc: |
| raise PyverbsRDMAError('Failed to query EQN', rc) |
| return eqn |
| |
| cdef add_ref(self, obj): |
| try: |
| Context.add_ref(self, obj) |
| except PyverbsError: |
| if isinstance(obj, Mlx5UMEM): |
| self.devx_umems.add(obj) |
| elif isinstance(obj, Mlx5DevxObj): |
| self.devx_objs.add(obj) |
| elif isinstance(obj, Mlx5DevxEq): |
| self.devx_eqs.add(obj) |
| else: |
| raise PyverbsError('Unrecognized object type') |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.context != NULL: |
| close_weakrefs([self.pps, self.devx_objs, self.devx_umems, self.devx_eqs]) |
| super(Mlx5Context, self).close() |
| |
| |
| cdef class Mlx5DVContext(PyverbsObject): |
| """ |
| Represents mlx5dv_context struct, which exposes mlx5-specific capabilities, |
| reported by mlx5dv_query_device. |
| """ |
| @property |
| def version(self): |
| return self.dv.version |
| |
| @property |
| def flags(self): |
| return self.dv.flags |
| |
| @property |
| def comp_mask(self): |
| return self.dv.comp_mask |
| @comp_mask.setter |
| def comp_mask(self, val): |
| self.dv.comp_mask = val |
| |
| @property |
| def cqe_comp_caps(self): |
| return self.dv.cqe_comp_caps |
| |
| @property |
| def sw_parsing_caps(self): |
| return self.dv.sw_parsing_caps |
| |
| @property |
| def striding_rq_caps(self): |
| return self.dv.striding_rq_caps |
| |
| @property |
| def tunnel_offload_caps(self): |
| return self.dv.tunnel_offloads_caps |
| |
| @property |
| def max_dynamic_bfregs(self): |
| return self.dv.max_dynamic_bfregs |
| |
| @property |
| def max_clock_info_update_nsec(self): |
| return self.dv.max_clock_info_update_nsec |
| |
| @property |
| def flow_action_flags(self): |
| return self.dv.flow_action_flags |
| |
| @property |
| def dc_odp_caps(self): |
| return self.dv.dc_odp_caps |
| |
| @property |
| def crypto_caps(self): |
| return self.dv.crypto_caps |
| |
| @property |
| def num_lag_ports(self): |
| return self.dv.num_lag_ports |
| |
| @property |
| def dci_streams_caps(self): |
| return self.dv.dci_streams_caps |
| |
| @property |
| def max_wr_memcpy_length(self): |
| return self.dv.max_wr_memcpy_length |
| |
| @property |
| def max_dc_rd_atom(self): |
| return self.dv.max_dc_rd_atom |
| |
| @property |
| def max_dc_init_rd_atom(self): |
| return self.dv.max_dc_init_rd_atom |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| ident_format = ' {:20}: {:<20}\n' |
| cqe = 'CQE compression caps:\n' +\ |
| ident_format.format('max num', |
| self.dv.cqe_comp_caps.max_num) +\ |
| ident_format.format('supported formats', |
| cqe_comp_to_str(self.dv.cqe_comp_caps.supported_format)) |
| swp = 'SW parsing caps:\n' +\ |
| ident_format.format('SW parsing offloads', |
| swp_to_str(self.dv.sw_parsing_caps.sw_parsing_offloads)) +\ |
| ident_format.format('supported QP types', |
| qpts_to_str(self.dv.sw_parsing_caps.supported_qpts)) |
| strd = 'Striding RQ caps:\n' +\ |
| ident_format.format('min single stride log num of bytes', |
| self.dv.striding_rq_caps.min_single_stride_log_num_of_bytes) +\ |
| ident_format.format('max single stride log num of bytes', |
| self.dv.striding_rq_caps.max_single_stride_log_num_of_bytes) +\ |
| ident_format.format('min single wqe log num of strides', |
| self.dv.striding_rq_caps.min_single_wqe_log_num_of_strides) +\ |
| ident_format.format('max single wqe log num of strides', |
| self.dv.striding_rq_caps.max_single_wqe_log_num_of_strides) +\ |
| ident_format.format('supported QP types', |
| qpts_to_str(self.dv.striding_rq_caps.supported_qpts)) |
| stream = 'DCI stream caps:\n' +\ |
| ident_format.format('max log num concurrent streams', |
| self.dv.dci_streams_caps.max_log_num_concurent) +\ |
| ident_format.format('max log num errored streams', |
| self.dv.dci_streams_caps.max_log_num_errored) |
| return print_format.format('Version', self.dv.version) +\ |
| print_format.format('Flags', |
| context_flags_to_str(self.dv.flags)) +\ |
| print_format.format('comp mask', |
| context_comp_mask_to_str(self.dv.comp_mask)) +\ |
| cqe + swp + strd + stream +\ |
| print_format.format('Tunnel offloads caps', |
| tunnel_offloads_to_str(self.dv.tunnel_offloads_caps)) +\ |
| print_format.format('Max dynamic BF registers', |
| self.dv.max_dynamic_bfregs) +\ |
| print_format.format('Max clock info update [nsec]', |
| self.dv.max_clock_info_update_nsec) +\ |
| print_format.format('Flow action flags', |
| self.dv.flow_action_flags) +\ |
| print_format.format('DC ODP caps', self.dv.dc_odp_caps) +\ |
| print_format.format('Num LAG ports', self.dv.num_lag_ports) +\ |
| print_format.format('Max WR memcpy length', self.dv.max_wr_memcpy_length) +\ |
| print_format.format('Max DC Read Atomic', self.dv.max_dc_rd_atomic) +\ |
| print_format.format('Max DC Init Read Atomic', self.dv.max_dc_init_rd_atomic) |
| |
| |
| cdef class Mlx5DCIStreamInitAttr(PyverbsObject): |
| """ |
| Represents dci_streams struct, which defines initial attributes |
| for DC QP creation. |
| """ |
| def __init__(self, log_num_concurent=0, log_num_errored=0): |
| """ |
| Initializes an Mlx5DCIStreamInitAttr object with the given DC |
| log_num_concurent and log_num_errored. |
| :param log_num_concurent: Number of dci stream channels. |
| :param log_num_errored: Number of dci error stream channels |
| before moving DCI to error. |
| :return: An initialized object |
| """ |
| super().__init__() |
| self.dci_streams.log_num_concurent = log_num_concurent |
| self.dci_streams.log_num_errored = log_num_errored |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('DCI Stream log_num_concurent', self.dci_streams.log_num_concurent) +\ |
| print_format.format('DCI Stream log_num_errored', self.dci_streams.log_num_errored) |
| |
| @property |
| def log_num_concurent(self): |
| return self.dci_streams.log_num_concurent |
| @log_num_concurent.setter |
| def log_num_concurent(self, val): |
| self.dci_streams.log_num_concurent=val |
| |
| @property |
| def log_num_errored(self): |
| return self.dci_streams.log_num_errored |
| @log_num_errored.setter |
| def log_num_errored(self, val): |
| self.dci_streams.log_num_errored=val |
| |
| |
| cdef class Mlx5DVDCInitAttr(PyverbsObject): |
| """ |
| Represents mlx5dv_dc_init_attr struct, which defines initial attributes |
| for DC QP creation. |
| """ |
| def __init__(self, dc_type=dve.MLX5DV_DCTYPE_DCI, dct_access_key=0, dci_streams=None): |
| """ |
| Initializes an Mlx5DVDCInitAttr object with the given DC type and DCT |
| access key. |
| :param dc_type: Which DC QP to create (DCI/DCT). |
| :param dct_access_key: Access key to be used by the DCT |
| :param dci_streams: Mlx5DCIStreamInitAttr |
| :return: An initializes object |
| """ |
| super().__init__() |
| self.attr.dc_type = dc_type |
| self.attr.dct_access_key = dct_access_key |
| if dci_streams is not None: |
| self.attr.dci_streams.log_num_concurent=dci_streams.log_num_concurent |
| self.attr.dci_streams.log_num_errored=dci_streams.log_num_errored |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('DC type', dc_type_to_str(self.attr.dc_type)) +\ |
| print_format.format('DCT access key', self.attr.dct_access_key) +\ |
| print_format.format('DCI Stream log_num_concurent', self.attr.dci_streams.log_num_concurent) +\ |
| print_format.format('DCI Stream log_num_errored', self.attr.dci_streams.log_num_errored) |
| |
| @property |
| def dc_type(self): |
| return self.attr.dc_type |
| @dc_type.setter |
| def dc_type(self, val): |
| self.attr.dc_type = val |
| |
| @property |
| def dct_access_key(self): |
| return self.attr.dct_access_key |
| @dct_access_key.setter |
| def dct_access_key(self, val): |
| self.attr.dct_access_key = val |
| |
| @property |
| def dci_streams(self): |
| return self.attr.dci_streams |
| @dci_streams.setter |
| def dci_streams(self, val): |
| self.attr.dci_streams=val |
| |
| cdef class Mlx5DVQPInitAttr(PyverbsObject): |
| """ |
| Represents mlx5dv_qp_init_attr struct, initial attributes used for mlx5 QP |
| creation. |
| """ |
| def __init__(self, comp_mask=0, create_flags=0, |
| Mlx5DVDCInitAttr dc_init_attr=None, send_ops_flags=0): |
| """ |
| Initializes an Mlx5DVQPInitAttr object with the given user data. |
| :param comp_mask: A bitmask specifying which fields are valid |
| :param create_flags: A bitwise OR of mlx5dv_qp_create_flags |
| :param dc_init_attr: Mlx5DVDCInitAttr object |
| :param send_ops_flags: A bitwise OR of mlx5dv_qp_create_send_ops_flags |
| :return: An initialized Mlx5DVQPInitAttr object |
| """ |
| super().__init__() |
| self.attr.comp_mask = comp_mask |
| self.attr.create_flags = create_flags |
| self.attr.send_ops_flags = send_ops_flags |
| if dc_init_attr is not None: |
| self.attr.dc_init_attr.dc_type = dc_init_attr.dc_type |
| if comp_mask & dve.MLX5DV_QP_INIT_ATTR_MASK_DCI_STREAMS: |
| self.attr.dc_init_attr.dci_streams = dc_init_attr.dci_streams |
| else: |
| self.attr.dc_init_attr.dct_access_key = dc_init_attr.dct_access_key |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('Comp mask', |
| qp_comp_mask_to_str(self.attr.comp_mask)) +\ |
| print_format.format('Create flags', |
| qp_create_flags_to_str(self.attr.create_flags)) +\ |
| 'DC init attr:\n' +\ |
| print_format.format(' DC type', |
| dc_type_to_str(self.attr.dc_init_attr.dc_type)) +\ |
| print_format.format(' DCI Stream log_num_concurent', |
| self.attr.dc_init_attr.dci_streams.log_num_concurent) +\ |
| print_format.format(' DCI Stream log_num_errored', |
| self.attr.dc_init_attr.dci_streams.log_num_errored) +\ |
| print_format.format(' DCT access key', |
| self.attr.dc_init_attr.dct_access_key) +\ |
| print_format.format('Send ops flags', |
| send_ops_flags_to_str(self.attr.send_ops_flags)) |
| |
| @property |
| def comp_mask(self): |
| return self.attr.comp_mask |
| @comp_mask.setter |
| def comp_mask(self, val): |
| self.attr.comp_mask = val |
| |
| @property |
| def create_flags(self): |
| return self.attr.create_flags |
| @create_flags.setter |
| def create_flags(self, val): |
| self.attr.create_flags = val |
| |
| @property |
| def send_ops_flags(self): |
| return self.attr.send_ops_flags |
| @send_ops_flags.setter |
| def send_ops_flags(self, val): |
| self.attr.send_ops_flags = val |
| |
| @property |
| def dc_type(self): |
| return self.attr.dc_init_attr.dc_type |
| @dc_type.setter |
| def dc_type(self, val): |
| self.attr.dc_init_attr.dc_type = val |
| |
| @property |
| def dct_access_key(self): |
| return self.attr.dc_init_attr.dct_access_key |
| @dct_access_key.setter |
| def dct_access_key(self, val): |
| self.attr.dc_init_attr.dct_access_key = val |
| |
| @property |
| def dci_streams(self): |
| return self.attr.dc_init_attr.dci_streams |
| @dci_streams.setter |
| def dci_streams(self, val): |
| self.attr.dc_init_attr.dci_streams=val |
| |
| |
| cdef copy_mr_interleaved_array(dv.mlx5dv_mr_interleaved *mr_interleaved_p, |
| mr_interleaved_lst): |
| """ |
| Build C array from the C objects of Mlx5MrInterleaved list and set the |
| mr_interleaved_p to this array address. The mr_interleaved_p should be |
| allocated with enough size for those objects. |
| :param mr_interleaved_p: Pointer to array of mlx5dv_mr_interleaved. |
| :param mr_interleaved_lst: List of Mlx5MrInterleaved. |
| """ |
| num_interleaved = len(mr_interleaved_lst) |
| cdef dv.mlx5dv_mr_interleaved *tmp |
| for i in range(num_interleaved): |
| tmp = &(<Mlx5MrInterleaved>mr_interleaved_lst[i]).mlx5dv_mr_interleaved |
| memcpy(mr_interleaved_p, tmp, sizeof(dv.mlx5dv_mr_interleaved)) |
| mr_interleaved_p += 1 |
| |
| |
| cdef class Mlx5QP(QPEx): |
| def __init__(self, Context context, QPInitAttrEx init_attr, |
| Mlx5DVQPInitAttr dv_init_attr): |
| """ |
| Initializes an mlx5 QP according to the user-provided data. |
| :param context: Context object |
| :param init_attr: QPInitAttrEx object |
| :param dv_init_attr: Mlx5DVQPInitAttr object |
| :return: An initialized Mlx5QP |
| """ |
| cdef PD pd |
| |
| # Initialize the logger here as the parent's __init__ is called after |
| # the QP is allocated. Allocation can fail, which will lead to exceptions |
| # thrown during object's teardown. |
| self.logger = logging.getLogger(self.__class__.__name__) |
| self.dc_type = dv_init_attr.dc_type if dv_init_attr else 0 |
| if init_attr.pd is not None: |
| pd = <PD>init_attr.pd |
| pd.add_ref(self) |
| self.qp = \ |
| dv.mlx5dv_create_qp(context.context, |
| &init_attr.attr, |
| &dv_init_attr.attr if dv_init_attr is not None |
| else NULL) |
| if self.qp == NULL: |
| raise PyverbsRDMAErrno('Failed to create MLX5 QP.\nQPInitAttrEx ' |
| 'attributes:\n{}\nMLX5DVQPInitAttr:\n{}'. |
| format(init_attr, dv_init_attr)) |
| super().__init__(context, init_attr) |
| |
| def _get_comp_mask(self, dst): |
| masks = {dve.MLX5DV_DCTYPE_DCT: {'INIT': e.IBV_QP_PKEY_INDEX | |
| e.IBV_QP_PORT | e.IBV_QP_ACCESS_FLAGS, |
| 'RTR': e.IBV_QP_AV |\ |
| e.IBV_QP_PATH_MTU |\ |
| e.IBV_QP_MIN_RNR_TIMER}, |
| dve.MLX5DV_DCTYPE_DCI: {'INIT': e.IBV_QP_PKEY_INDEX |\ |
| e.IBV_QP_PORT, |
| 'RTR': e.IBV_QP_PATH_MTU, |
| 'RTS': e.IBV_QP_TIMEOUT |\ |
| e.IBV_QP_RETRY_CNT |\ |
| e.IBV_QP_RNR_RETRY | e.IBV_QP_SQ_PSN |\ |
| e.IBV_QP_MAX_QP_RD_ATOMIC}} |
| if self.dc_type == 0: |
| return super()._get_comp_mask(dst) |
| return masks[self.dc_type][dst] | e.IBV_QP_STATE |
| |
| def wr_set_dc_addr(self, AH ah, remote_dctn, remote_dc_key): |
| """ |
| Attach a DC info to the last work request. |
| :param ah: Address Handle to the requested DCT. |
| :param remote_dctn: The remote DCT number. |
| :param remote_dc_key: The remote DC key. |
| """ |
| dv.mlx5dv_wr_set_dc_addr(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| ah.ah, remote_dctn, remote_dc_key) |
| |
| def wr_raw_wqe(self, wqe): |
| """ |
| Build a raw work request |
| :param wqe: A Wqe object |
| """ |
| cdef void *wqe_ptr = <void *> <uintptr_t> wqe.address |
| dv.mlx5dv_wr_raw_wqe(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), wqe_ptr) |
| |
| def wr_mr_interleaved(self, Mlx5Mkey mkey, access_flags, repeat_count, |
| mr_interleaved_lst): |
| """ |
| Registers an interleaved memory layout by using an indirect mkey and |
| some interleaved data. |
| :param mkey: A Mlx5Mkey instance to reg this memory. |
| :param access_flags: The mkey access flags. |
| :param repeat_count: Number of times to repeat the interleaved layout. |
| :param mr_interleaved_lst: List of Mlx5MrInterleaved. |
| """ |
| num_interleaved = len(mr_interleaved_lst) |
| cdef dv.mlx5dv_mr_interleaved *mr_interleaved_p = \ |
| <dv.mlx5dv_mr_interleaved*>calloc(1, num_interleaved * sizeof(dv.mlx5dv_mr_interleaved)) |
| if mr_interleaved_p == NULL: |
| raise MemoryError('Failed to calloc mr interleaved buffers') |
| copy_mr_interleaved_array(mr_interleaved_p, mr_interleaved_lst) |
| dv.mlx5dv_wr_mr_interleaved(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| mkey.mlx5dv_mkey, access_flags, repeat_count, |
| num_interleaved, mr_interleaved_p) |
| free(mr_interleaved_p) |
| |
| def wr_mr_list(self, Mlx5Mkey mkey, access_flags, sge_list): |
| """ |
| Registers a memory layout based on list of SGE. |
| :param mkey: A Mlx5Mkey instance to reg this memory. |
| :param access_flags: The mkey access flags. |
| :param sge_list: List of SGE. |
| """ |
| num_sges = len(sge_list) |
| cdef v.ibv_sge *sge_p = <v.ibv_sge*>calloc(1, num_sges * sizeof(v.ibv_sge)) |
| if sge_p == NULL: |
| raise MemoryError('Failed to calloc sge buffers') |
| copy_sg_array(sge_p, sge_list, num_sges) |
| dv.mlx5dv_wr_mr_list(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| mkey.mlx5dv_mkey, access_flags, num_sges, sge_p) |
| free(sge_p) |
| |
| def wr_mkey_configure(self, Mlx5Mkey mkey, num_setters, Mlx5MkeyConfAttr mkey_config): |
| """ |
| Create a work request to configure an Mkey |
| :param mkey: A Mlx5Mkey instance to configure. |
| :param num_setters: The number of setters that must be called after |
| this function. |
| :param attr: The Mkey configuration attributes. |
| """ |
| dv.mlx5dv_wr_mkey_configure(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| mkey.mlx5dv_mkey, num_setters, |
| &mkey_config.mlx5dv_mkey_conf_attr) |
| |
| def wr_set_mkey_access_flags(self, access_flags): |
| """ |
| Set the memory protection attributes for an Mkey |
| :param access_flags: The mkey access flags. |
| """ |
| dv.mlx5dv_wr_set_mkey_access_flags(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| access_flags) |
| |
| def wr_set_mkey_layout_list(self, sge_list): |
| """ |
| Set a memory layout for an Mkey based on SGE list. |
| :param sge_list: List of SGE. |
| """ |
| num_sges = len(sge_list) |
| cdef v.ibv_sge *sge_p = <v.ibv_sge*>calloc(1, num_sges * sizeof(v.ibv_sge)) |
| if sge_p == NULL: |
| raise MemoryError('Failed to calloc sge buffers') |
| copy_sg_array(sge_p, sge_list, num_sges) |
| dv.mlx5dv_wr_set_mkey_layout_list(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| num_sges, sge_p) |
| free(sge_p) |
| |
| def wr_set_mkey_layout_interleaved(self, repeat_count, mr_interleaved_lst): |
| """ |
| Set an interleaved memory layout for an Mkey |
| :param repeat_count: Number of times to repeat the interleaved layout. |
| :param mr_interleaved_lst: List of Mlx5MrInterleaved. |
| """ |
| num_interleaved = len(mr_interleaved_lst) |
| cdef dv.mlx5dv_mr_interleaved *mr_interleaved_p = \ |
| <dv.mlx5dv_mr_interleaved*>calloc(1, num_interleaved * sizeof(dv.mlx5dv_mr_interleaved)) |
| if mr_interleaved_p == NULL: |
| raise MemoryError('Failed to calloc mr interleaved buffers') |
| copy_mr_interleaved_array(mr_interleaved_p, mr_interleaved_lst) |
| dv.mlx5dv_wr_set_mkey_layout_interleaved(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| repeat_count, num_interleaved, |
| mr_interleaved_p) |
| free(mr_interleaved_p) |
| |
| def wr_set_mkey_crypto(self, Mlx5CryptoAttr attr): |
| """ |
| Configure a MKey for crypto operation. |
| :param attr: crypto attributes to set for the mkey. |
| """ |
| dv.mlx5dv_wr_set_mkey_crypto(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| &attr.mlx5dv_crypto_attr) |
| |
| def wr_set_mkey_sig_block(self, Mlx5SigBlockAttr block_attr): |
| """ |
| Configure a MKEY for block signature (data integrity) operation. |
| :param block_attr: Block signature attributes to set for the mkey. |
| """ |
| dv.mlx5dv_wr_set_mkey_sig_block(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| &block_attr.mlx5dv_sig_block_attr) |
| |
| def wr_memcpy(self, dest_lkey, dest_addr, src_lkey, src_addr, length): |
| """ |
| Copies memory data on PCI bus using DMA functionality of the device. |
| :param dest_lkey: Local key of the mkey to copy data to |
| :param dest_addr: Memory address to copy data to |
| :param src_lkey: Local key of the mkey to copy data from |
| :param src_addr: Memory address to copy data from |
| :param length: Length of data to be copied |
| """ |
| dv.mlx5dv_wr_memcpy(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| dest_lkey, dest_addr, src_lkey, src_addr, length) |
| |
| def cancel_posted_send_wrs(self, wr_id): |
| """ |
| Cancel all pending send work requests with supplied wr_id in a QP in |
| SQD state. |
| :param wr_id: The WRID to cancel. |
| :return: Number of work requests that were canceled. |
| """ |
| rc = dv.mlx5dv_qp_cancel_posted_send_wrs(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| wr_id) |
| if rc < 0: |
| raise PyverbsRDMAError(f'Failed to cancel send WRs', -rc) |
| return rc |
| |
| def wr_set_dc_addr_stream(self, AH ah, remote_dctn, remote_dc_key, stream_id): |
| """ |
| Attach a DC info to the last work request. |
| :param ah: Address Handle to the requested DCT. |
| :param remote_dctn: The remote DCT number. |
| :param remote_dc_key: The remote DC key. |
| :param stream_id: DCI stream channel_id |
| """ |
| dv.mlx5dv_wr_set_dc_addr_stream(dv.mlx5dv_qp_ex_from_ibv_qp_ex(self.qp_ex), |
| ah.ah, remote_dctn, remote_dc_key, |
| stream_id) |
| |
| @staticmethod |
| def query_lag_port(QP qp): |
| """ |
| Queries for port num that the QP desired to use, and the port that |
| is currently used by the bond for this QP. |
| :param qp: Queries the port for this QP. |
| :return: Tuple of the desired port and actual port which used by the HW. |
| """ |
| cdef uint8_t port_num |
| cdef uint8_t active_port_num |
| rc = dv.mlx5dv_query_qp_lag_port(qp.qp, &port_num, &active_port_num) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to query QP #{qp.qp.qp_num}', rc) |
| return port_num, active_port_num |
| |
| @staticmethod |
| def modify_lag_port(QP qp, uint8_t port_num): |
| """ |
| Modifies the lag port num that the QP desires to use. |
| :param qp: Modifies the port for this QP. |
| :param port_num: The desired port to be used by the QP to send traffic |
| in a LAG configuration. |
| """ |
| rc = dv.mlx5dv_modify_qp_lag_port(qp.qp, port_num) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to modify lag of QP #{qp.qp.qp_num}', rc) |
| |
| @staticmethod |
| def modify_qp_sched_elem(QP qp, Mlx5dvSchedLeaf req_sched_leaf=None, |
| Mlx5dvSchedLeaf resp_sched_leaf=None): |
| """ |
| Connect a QP with a requestor and/or a responder scheduling element. |
| :param qp: connect this QP to schedule elements. |
| :param req_sched_leaf: Mlx5dvSchedLeaf for the send queue. |
| :param resp_sched_leaf: Mlx5dvSchedLeaf for the recv queue. |
| """ |
| req_se = req_sched_leaf.sched_leaf if req_sched_leaf else NULL |
| resp_se = resp_sched_leaf.sched_leaf if resp_sched_leaf else NULL |
| rc = dv.mlx5dv_modify_qp_sched_elem(qp.qp, req_se, resp_se) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to modify QP #{qp.qp.qp_num} sched element', rc) |
| |
| @staticmethod |
| def modify_udp_sport(QP qp, uint16_t udp_sport): |
| """ |
| Modifies the UDP source port of a given QP. |
| :param qp: A QP in RTS state to modify its UDP sport. |
| :param udp_sport: The desired UDP sport to be used by the QP. |
| """ |
| rc = dv.mlx5dv_modify_qp_udp_sport(qp.qp, udp_sport) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to modify UDP source port of QP ' |
| f'#{qp.qp.qp_num}', rc) |
| |
| @staticmethod |
| def map_ah_to_qp(AH ah, qp_num): |
| """ |
| Map the destination path information in ah to the information extracted |
| from the qp. |
| :param ah: The target’s address handle. |
| :param qp_num: The traffic initiator QP number. |
| """ |
| rc = dv.mlx5dv_map_ah_to_qp(ah.ah, qp_num) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to map AH to QP #{qp_num}', rc) |
| |
| @staticmethod |
| def modify_dci_stream_channel_id(QP qp, uint16_t stream_id): |
| """ |
| Reset an errored stream_id in the HW DCI context. |
| :param qp: A DCI QP in RTS state. |
| :param stream_id: The desired stream_id that need to be reset. |
| """ |
| rc = dv.mlx5dv_dci_stream_id_reset(qp.qp, stream_id) |
| if rc != 0: |
| raise PyverbsRDMAError(f'Failed to reset stream_id #{stream_id} for DCI QP' |
| f'#{qp.qp.qp_num}', rc) |
| |
| |
| cdef class Mlx5DVCQInitAttr(PyverbsObject): |
| """ |
| Represents mlx5dv_cq_init_attr struct, initial attributes used for mlx5 CQ |
| creation. |
| """ |
| def __init__(self, comp_mask=0, cqe_comp_res_format=0, flags=0, cqe_size=0): |
| """ |
| Initializes an Mlx5CQInitAttr object with zeroes as default values. |
| :param comp_mask: Marks which of the following fields should be |
| considered. Use mlx5dv_cq_init_attr_mask enum. |
| :param cqe_comp_res_format: The various CQE response formats of the |
| responder side. Use |
| mlx5dv_cqe_comp_res_format enum. |
| :param flags: A bitwise OR of the various values described in |
| mlx5dv_cq_init_attr_flags. |
| :param cqe_size: Configure the CQE size to be 64 or 128 bytes, other |
| values will cause the CQ creation process to fail. |
| Valid when MLX5DV_CQ_INIT_ATTR_MASK_CQE_SIZE is set. |
| :return: None |
| """ |
| super().__init__() |
| self.attr.comp_mask = comp_mask |
| self.attr.cqe_comp_res_format = cqe_comp_res_format |
| self.attr.flags = flags |
| self.attr.cqe_size = cqe_size |
| |
| @property |
| def comp_mask(self): |
| return self.attr.comp_mask |
| @comp_mask.setter |
| def comp_mask(self, val): |
| self.attr.comp_mask = val |
| |
| @property |
| def cqe_comp_res_format(self): |
| return self.attr.cqe_comp_res_format |
| @cqe_comp_res_format.setter |
| def cqe_comp_res_format(self, val): |
| self.attr.cqe_comp_res_format = val |
| |
| @property |
| def flags(self): |
| return self.attr.flags |
| @flags.setter |
| def flags(self, val): |
| self.attr.flags = val |
| |
| @property |
| def cqe_size(self): |
| return self.attr.cqe_size |
| @cqe_size.setter |
| def cqe_size(self, val): |
| self.attr.cqe_size = val |
| |
| def __str__(self): |
| print_format = '{:22}: {:<20}\n' |
| flags = {dve.MLX5DV_CQ_INIT_ATTR_FLAGS_CQE_PAD: |
| "MLX5DV_CQ_INIT_ATTR_FLAGS_CQE_PAD}"} |
| mask = {dve.MLX5DV_CQ_INIT_ATTR_MASK_COMPRESSED_CQE: |
| "MLX5DV_CQ_INIT_ATTR_MASK_COMPRESSED_CQE", |
| dve.MLX5DV_CQ_INIT_ATTR_MASK_FLAGS: |
| "MLX5DV_CQ_INIT_ATTR_MASK_FLAGS", |
| dve.MLX5DV_CQ_INIT_ATTR_MASK_CQE_SIZE: |
| "MLX5DV_CQ_INIT_ATTR_MASK_CQE_SIZE"} |
| fmt = {dve.MLX5DV_CQE_RES_FORMAT_HASH: "MLX5DV_CQE_RES_FORMAT_HASH", |
| dve.MLX5DV_CQE_RES_FORMAT_CSUM: "MLX5DV_CQE_RES_FORMAT_CSUM", |
| dve.MLX5DV_CQE_RES_FORMAT_CSUM_STRIDX: |
| "MLX5DV_CQE_RES_FORMAT_CSUM_STRIDX"} |
| |
| return 'Mlx5DVCQInitAttr:\n' +\ |
| print_format.format('comp_mask', bitmask_to_str(self.comp_mask, |
| mask)) +\ |
| print_format.format('CQE compression format', |
| bitmask_to_str(self.cqe_comp_res_format, |
| fmt)) +\ |
| print_format.format('flags', bitmask_to_str(self.flags, |
| flags)) + \ |
| print_format.format('CQE size', self.cqe_size) |
| |
| |
| cdef class Mlx5CQ(CQEX): |
| def __init__(self, Mlx5Context context, CqInitAttrEx init_attr, |
| Mlx5DVCQInitAttr dv_init_attr): |
| # Initialize the logger here as the parent's __init__ is called after |
| # the CQ is allocated. Allocation can fail, which will lead to exceptions |
| # thrown during object's teardown. |
| self.logger = logging.getLogger(self.__class__.__name__) |
| self.cq = \ |
| dv.mlx5dv_create_cq(context.context, &init_attr.attr, |
| &dv_init_attr.attr if dv_init_attr is not None |
| else NULL) |
| if self.cq == NULL: |
| raise PyverbsRDMAErrno('Failed to create MLX5 CQ.\nCQInitAttrEx:\n' |
| '{}\nMLX5DVCQInitAttr:\n{}'. |
| format(init_attr, dv_init_attr)) |
| self.ibv_cq = v.ibv_cq_ex_to_cq(self.cq) |
| self.context = context |
| context.add_ref(self) |
| super().__init__(context, init_attr) |
| |
| def __str__(self): |
| print_format = '{:<22}: {:<20}\n' |
| return 'Mlx5 CQ:\n' +\ |
| print_format.format('Handle', self.cq.handle) +\ |
| print_format.format('CQEs', self.cq.cqe) |
| |
| |
| def qpts_to_str(qp_types): |
| numeric_types = qp_types |
| qpts_str = '' |
| qpts = {e.IBV_QPT_RC: 'RC', e.IBV_QPT_UC: 'UC', e.IBV_QPT_UD: 'UD', |
| e.IBV_QPT_RAW_PACKET: 'Raw Packet', e.IBV_QPT_XRC_SEND: 'XRC Send', |
| e.IBV_QPT_XRC_RECV: 'XRC Recv', e.IBV_QPT_DRIVER: 'Driver QPT'} |
| for t in qpts.keys(): |
| if (1 << t) & qp_types: |
| qpts_str += qpts[t] + ', ' |
| qp_types -= t |
| if qp_types == 0: |
| break |
| return qpts_str[:-2] + ' ({})'.format(numeric_types) |
| |
| |
| def bitmask_to_str(bits, values): |
| numeric_bits = bits |
| res = '' |
| for t in values.keys(): |
| if t & bits: |
| res += values[t] + ', ' |
| bits -= t |
| if bits == 0: |
| break |
| return res[:-2] + ' ({})'.format(numeric_bits) # Remove last comma and space |
| |
| |
| def context_comp_mask_to_str(mask): |
| l = {dve.MLX5DV_CONTEXT_MASK_CQE_COMPRESION: 'CQE compression', |
| dve.MLX5DV_CONTEXT_MASK_SWP: 'SW parsing', |
| dve.MLX5DV_CONTEXT_MASK_STRIDING_RQ: 'Striding RQ', |
| dve.MLX5DV_CONTEXT_MASK_TUNNEL_OFFLOADS: 'Tunnel offloads', |
| dve.MLX5DV_CONTEXT_MASK_DYN_BFREGS: 'Dynamic BF regs', |
| dve.MLX5DV_CONTEXT_MASK_CLOCK_INFO_UPDATE: 'Clock info update', |
| dve.MLX5DV_CONTEXT_MASK_FLOW_ACTION_FLAGS: 'Flow action flags'} |
| return bitmask_to_str(mask, l) |
| |
| |
| def context_flags_to_str(flags): |
| l = {dve.MLX5DV_CONTEXT_FLAGS_CQE_V1: 'CQE v1', |
| dve.MLX5DV_CONTEXT_FLAGS_MPW_ALLOWED: 'Multi packet WQE allowed', |
| dve.MLX5DV_CONTEXT_FLAGS_ENHANCED_MPW: 'Enhanced multi packet WQE', |
| dve.MLX5DV_CONTEXT_FLAGS_CQE_128B_COMP: 'Support CQE 128B compression', |
| dve.MLX5DV_CONTEXT_FLAGS_CQE_128B_PAD: 'Support CQE 128B padding', |
| dve.MLX5DV_CONTEXT_FLAGS_PACKET_BASED_CREDIT_MODE: |
| 'Support packet based credit mode (in RC QP)'} |
| return bitmask_to_str(flags, l) |
| |
| |
| def swp_to_str(swps): |
| l = {dve.MLX5DV_SW_PARSING: 'SW Parsing', |
| dve.MLX5DV_SW_PARSING_CSUM: 'SW Parsing CSUM', |
| dve.MLX5DV_SW_PARSING_LSO: 'SW Parsing LSO'} |
| return bitmask_to_str(swps, l) |
| |
| |
| def cqe_comp_to_str(cqe): |
| l = {dve.MLX5DV_CQE_RES_FORMAT_HASH: 'with hash', |
| dve.MLX5DV_CQE_RES_FORMAT_CSUM: 'with RX checksum CSUM', |
| dve.MLX5DV_CQE_RES_FORMAT_CSUM_STRIDX: 'with stride index'} |
| return bitmask_to_str(cqe, l) |
| |
| |
| def tunnel_offloads_to_str(tun): |
| l = {dve.MLX5DV_RAW_PACKET_CAP_TUNNELED_OFFLOAD_VXLAN: 'VXLAN', |
| dve.MLX5DV_RAW_PACKET_CAP_TUNNELED_OFFLOAD_GRE: 'GRE', |
| dve.MLX5DV_RAW_PACKET_CAP_TUNNELED_OFFLOAD_GENEVE: 'Geneve', |
| dve.MLX5DV_RAW_PACKET_CAP_TUNNELED_OFFLOAD_CW_MPLS_OVER_GRE:\ |
| 'Ctrl word + MPLS over GRE', |
| dve.MLX5DV_RAW_PACKET_CAP_TUNNELED_OFFLOAD_CW_MPLS_OVER_UDP:\ |
| 'Ctrl word + MPLS over UDP'} |
| return bitmask_to_str(tun, l) |
| |
| |
| def dc_type_to_str(dctype): |
| l = {dve.MLX5DV_DCTYPE_DCT: 'DCT', dve.MLX5DV_DCTYPE_DCI: 'DCI'} |
| try: |
| return l[dctype] |
| except KeyError: |
| return 'Unknown DC type ({dc})'.format(dc=dctype) |
| |
| |
| def qp_comp_mask_to_str(flags): |
| l = {dve.MLX5DV_QP_INIT_ATTR_MASK_QP_CREATE_FLAGS: 'Create flags', |
| dve.MLX5DV_QP_INIT_ATTR_MASK_DC: 'DC', |
| dve.MLX5DV_QP_INIT_ATTR_MASK_SEND_OPS_FLAGS: 'Send ops flags', |
| dve.MLX5DV_QP_INIT_ATTR_MASK_DCI_STREAMS: 'DCI Stream'} |
| return bitmask_to_str(flags, l) |
| |
| |
| def qp_create_flags_to_str(flags): |
| l = {dve.MLX5DV_QP_CREATE_TUNNEL_OFFLOADS: 'Tunnel offloads', |
| dve.MLX5DV_QP_CREATE_TIR_ALLOW_SELF_LOOPBACK_UC: |
| 'Allow UC self loopback', |
| dve.MLX5DV_QP_CREATE_TIR_ALLOW_SELF_LOOPBACK_MC: |
| 'Allow MC self loopback', |
| dve.MLX5DV_QP_CREATE_DISABLE_SCATTER_TO_CQE: 'Disable scatter to CQE', |
| dve.MLX5DV_QP_CREATE_ALLOW_SCATTER_TO_CQE: 'Allow scatter to CQE', |
| dve.MLX5DV_QP_CREATE_PACKET_BASED_CREDIT_MODE: |
| 'Packet based credit mode', |
| dve.MLX5DV_QP_CREATE_SIG_PIPELINING: 'Support signature pipeline support'} |
| return bitmask_to_str(flags, l) |
| |
| |
| def send_ops_flags_to_str(flags): |
| l = {dve.MLX5DV_QP_EX_WITH_MR_INTERLEAVED: 'With MR interleaved', |
| dve.MLX5DV_QP_EX_WITH_MR_LIST: 'With MR list', |
| dve.MLX5DV_QP_EX_WITH_MKEY_CONFIGURE: 'With Mkey configure'} |
| return bitmask_to_str(flags, l) |
| |
| |
| cdef class Mlx5VAR(PyverbsObject): |
| def __init__(self, Context context not None, flags=0): |
| self.context = context |
| self.var = dv.mlx5dv_alloc_var(context.context, flags) |
| if self.var == NULL: |
| raise PyverbsRDMAErrno('Failed to allocate VAR') |
| context.vars.add(self) |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.var != NULL: |
| dv.mlx5dv_free_var(self.var) |
| self.var = NULL |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('page id', self.var.page_id) +\ |
| print_format.format('length', self.var.length) +\ |
| print_format.format('mmap offset', self.var.mmap_off) +\ |
| print_format.format('compatibility mask', self.var.comp_mask) |
| |
| @property |
| def page_id(self): |
| return self.var.page_id |
| |
| @property |
| def length(self): |
| return self.var.length |
| |
| @property |
| def mmap_off(self): |
| return self.var.mmap_off |
| |
| @property |
| def comp_mask(self): |
| return self.var.comp_mask |
| |
| |
| cdef class Mlx5PP(PyverbsObject): |
| """ |
| Represents mlx5dv_pp, packet pacing struct. |
| """ |
| def __init__(self, Context context not None, pp_context, flags=0): |
| """ |
| Initializes a Mlx5PP object. |
| :param context: DevX context |
| :param pp_context: Bytes of packet pacing context according to the |
| device specs. Must be bytes type or implements |
| __bytes__ method |
| :param flags: Packet pacing allocation flags |
| """ |
| self.context = context |
| pp_ctx_bytes = bytes(pp_context) |
| self.pp = dv.mlx5dv_pp_alloc(context.context, len(pp_ctx_bytes), |
| <char*>pp_ctx_bytes, flags) |
| if self.pp == NULL: |
| raise PyverbsRDMAErrno('Failed to allocate packet pacing entry') |
| context.pps.add(self) |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.pp != NULL: |
| dv.mlx5dv_pp_free(self.pp) |
| self.pp = NULL |
| |
| @property |
| def index(self): |
| return self.pp.index |
| |
| |
| cdef class Mlx5UAR(PyverbsObject): |
| def __init__(self, Context context not None, flags=0): |
| self.uar = dv.mlx5dv_devx_alloc_uar(context.context, flags) |
| if self.uar == NULL: |
| raise PyverbsRDMAErrno('Failed to allocate UAR') |
| context.uars.add(self) |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.uar != NULL: |
| dv.mlx5dv_devx_free_uar(self.uar) |
| self.uar = NULL |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('reg addr', <uintptr_t>self.uar.reg_addr) +\ |
| print_format.format('base addr', <uintptr_t>self.uar.base_addr) +\ |
| print_format.format('page id', self.uar.page_id) +\ |
| print_format.format('mmap off', self.uar.mmap_off) +\ |
| print_format.format('comp mask', self.uar.comp_mask) |
| |
| @property |
| def reg_addr(self): |
| return <uintptr_t>self.uar.reg_addr |
| |
| @property |
| def base_addr(self): |
| return <uintptr_t>self.uar.base_addr |
| |
| @property |
| def page_id(self): |
| return self.uar.page_id |
| |
| @property |
| def mmap_off(self): |
| return self.uar.mmap_off |
| |
| @property |
| def comp_mask(self): |
| return self.uar.comp_mask |
| |
| @property |
| def uar(self): |
| return <uintptr_t>self.uar |
| |
| |
| cdef class Mlx5DmOpAddr(PyverbsCM): |
| def __init__(self, DM dm not None, op=0): |
| """ |
| Wraps mlx5dv_dm_map_op_addr. |
| Gets operation address of a device memory (DM), which must be munmapped by |
| the user when it's no longer needed. |
| :param dm: Device Memory instance |
| :param op: DM operation type |
| :return: An mmaped address to the DM for the requested operation (op). |
| """ |
| self.addr = dv.mlx5dv_dm_map_op_addr(dm.dm, op) |
| if self.addr == NULL: |
| raise PyverbsRDMAErrno('Failed to get DM operation address') |
| |
| def unmap(self, length): |
| munmap(self.addr, length) |
| |
| def write(self, data): |
| """ |
| Writes data (bytes) to the DM operation address using memcpy. |
| :param data: Bytes of data |
| """ |
| memcpy(<char *>self.addr, <char *>data, len(data)) |
| |
| def read(self, length): |
| """ |
| Reads 'length' bytes from the DM operation address using memcpy. |
| :param length: Data length to read (in bytes) |
| :return: Read data in bytes |
| """ |
| cdef char *data = <char*> calloc(length, sizeof(char)) |
| if data == NULL: |
| raise PyverbsError('Failed to allocate memory') |
| memcpy(<char *>data, <char *>self.addr, length) |
| res = data[:length] |
| free(data) |
| return res |
| |
| cpdef close(self): |
| self.addr = NULL |
| |
| @property |
| def addr(self): |
| return <uintptr_t>self.addr |
| |
| |
| cdef class WqeSeg(PyverbsCM): |
| """ |
| An abstract class for WQE segments. |
| Each WQE segment (such as control segment, data segment, etc.) should |
| inherit from this class. |
| """ |
| |
| @staticmethod |
| def sizeof(): |
| return 0 |
| |
| cpdef _copy_to_buffer(self, addr): |
| memcpy(<void *><uintptr_t>addr, <void *>self.segment, self.sizeof()) |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.segment != NULL: |
| free(self.segment) |
| self.segment = NULL |
| |
| |
| cdef class WqeCtrlSeg(WqeSeg): |
| """ |
| Wrapper class for dv.mlx5_wqe_ctrl_seg |
| """ |
| |
| def __init__(self, pi=0, opcode=0, opmod=0, qp_num=0, fm_ce_se=0, ds=0, |
| signature=0, imm=0): |
| """ |
| Create a WqeCtrlSeg by creating a mlx5_wqe_ctrl_seg and |
| using mlx5dv_set_ctrl_seg, segment values are accessed |
| through the getters/setters. |
| """ |
| self.segment = calloc(sizeof(dv.mlx5_wqe_ctrl_seg), 1) |
| self.set_ctrl_seg(pi, opcode, opmod, qp_num, fm_ce_se, ds, signature, imm) |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('opcode', |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).opmod_idx_opcode) + \ |
| print_format.format('qpn_ds', |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).qpn_ds) + \ |
| print_format.format('signature', |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).signature) + \ |
| print_format.format('fm_ce_se', |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).fm_ce_se) + \ |
| print_format.format('imm', |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).imm) |
| |
| def set_ctrl_seg(self, pi, opcode, opmod, qp_num, fm_ce_se, ds, signature, imm): |
| dv.mlx5dv_set_ctrl_seg(<dv.mlx5_wqe_ctrl_seg *>self.segment, pi, opcode, |
| opmod, qp_num, fm_ce_se, ds, signature, imm) |
| |
| @staticmethod |
| def sizeof(): |
| return sizeof(dv.mlx5_wqe_ctrl_seg) |
| |
| @property |
| def addr(self): |
| return <uintptr_t>self.segment |
| |
| @property |
| def opmod_idx_opcode(self): |
| return be32toh((<dv.mlx5_wqe_ctrl_seg *>self.segment).opmod_idx_opcode) |
| @opmod_idx_opcode.setter |
| def opmod_idx_opcode(self, val): |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).opmod_idx_opcode = htobe32(val) |
| |
| @property |
| def qpn_ds(self): |
| return be32toh((<dv.mlx5_wqe_ctrl_seg *>self.segment).qpn_ds) |
| @qpn_ds.setter |
| def qpn_ds(self, val): |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).qpn_ds = htobe32(val) |
| |
| @property |
| def signature(self): |
| return (<dv.mlx5_wqe_ctrl_seg *>self.segment).signature |
| @signature.setter |
| def signature(self, val): |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).signature = val |
| |
| @property |
| def fm_ce_se(self): |
| return (<dv.mlx5_wqe_ctrl_seg *>self.segment).fm_ce_se |
| @fm_ce_se.setter |
| def fm_ce_se(self, val): |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).fm_ce_se = val |
| |
| @property |
| def imm(self): |
| return be32toh((<dv.mlx5_wqe_ctrl_seg *>self.segment).imm) |
| @imm.setter |
| def imm(self, val): |
| (<dv.mlx5_wqe_ctrl_seg *>self.segment).imm = htobe32(val) |
| |
| |
| cdef class WqeDataSeg(WqeSeg): |
| |
| def __init__(self, length=0, lkey=0, addr=0): |
| """ |
| Create a dv.mlx5_wqe_data_seg by allocating it and using |
| dv.mlx5dv_set_data_seg with the values received in init |
| """ |
| self.segment = calloc(sizeof(dv.mlx5_wqe_data_seg), 1) |
| self.set_data_seg(length, lkey, addr) |
| |
| @staticmethod |
| def sizeof(): |
| return sizeof(dv.mlx5_wqe_data_seg) |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('byte_count', |
| (<dv.mlx5_wqe_data_seg *>self.segment).byte_count) + \ |
| print_format.format('lkey', (<dv.mlx5_wqe_data_seg *>self.segment).lkey) + \ |
| print_format.format('addr', (<dv.mlx5_wqe_data_seg *>self.segment).addr) |
| |
| def set_data_seg(self, length, lkey, addr): |
| dv.mlx5dv_set_data_seg(<dv.mlx5_wqe_data_seg *>self.segment, |
| length, lkey, addr) |
| |
| @property |
| def byte_count(self): |
| return be32toh((<dv.mlx5_wqe_data_seg *>self.segment).byte_count) |
| @byte_count.setter |
| def byte_count(self, val): |
| (<dv.mlx5_wqe_data_seg *>self.segment).byte_count = htobe32(val) |
| |
| @property |
| def lkey(self): |
| return be32toh((<dv.mlx5_wqe_data_seg *>self.segment).lkey) |
| @lkey.setter |
| def lkey(self, val): |
| (<dv.mlx5_wqe_data_seg *>self.segment).lkey = htobe32(val) |
| |
| @property |
| def addr(self): |
| return be64toh((<dv.mlx5_wqe_data_seg *>self.segment).addr) |
| @addr.setter |
| def addr(self, val): |
| (<dv.mlx5_wqe_data_seg *>self.segment).addr = htobe64(val) |
| |
| |
| cdef class Wqe(PyverbsCM): |
| """ |
| The Wqe class represents a WQE, which is one or more chained WQE segments. |
| """ |
| |
| def __init__(self, segments, addr=0): |
| """ |
| Create a Wqe with <segments>, in case an address <addr> was not passed |
| by the user, memory would be allocated according to the size needed and |
| the segments are copied over to the buffer. |
| :param segments: The segments (ctrl, data) of the Wqe as PRM format or |
| WqeSeg instance. |
| :param addr: User address to write the WQE on (Optional) |
| """ |
| self.segments = segments |
| if addr: |
| self.is_user_addr = True |
| self.addr = <void*><uintptr_t> addr |
| else: |
| self.is_user_addr = False |
| allocation_size = sum(map(lambda x: x.sizeof() if isinstance(x, WqeSeg) else len(x), |
| self.segments)) |
| self.addr = calloc(allocation_size, 1) |
| addr = <uintptr_t>self.addr |
| for seg in self.segments: |
| if isinstance(seg, WqeSeg): |
| seg._copy_to_buffer(addr) |
| addr += seg.sizeof() |
| else: # PRM format |
| addr = copy_data_to_addr(addr, seg) |
| |
| @property |
| def address(self): |
| return <uintptr_t>self.addr |
| |
| def __str__(self): |
| ret_str = '' |
| i = 0 |
| for segment in self.segments: |
| ret_str += f'Segment type {type(segment)} #{i}:\n' + str(segment) |
| i += 1 |
| return ret_str |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.addr != NULL: |
| if not self.is_user_addr: |
| free(self.addr) |
| self.addr = NULL |
| |
| |
| cdef class Mlx5UMEM(PyverbsCM): |
| def __init__(self, Context context not None, size, addr=None, alignment=64, |
| access=0, pgsz_bitmap=0, comp_mask=0, dmabuf_fd=0): |
| """ |
| User memory object to be used by the DevX interface. |
| If pgsz_bitmap or comp_mask were passed, the extended umem registration |
| will be used. |
| :param context: RDMA device context to create the action on |
| :param size: The size of the addr buffer (or the internal buffer to be |
| allocated if addr is None) |
| :param alignment: The alignment of the internally allocated buffer |
| (Valid if addr is None) |
| :param addr: The memory start address to register (if None, the address |
| will be allocated internally) |
| :param access: The desired memory protection attributes (default: 0) |
| :param pgsz_bitmap: Represents the required page sizes |
| :param comp_mask: Compatibility mask |
| :param dmabuf_fd: FD of a dmabuf |
| """ |
| super().__init__() |
| cdef dv.mlx5dv_devx_umem_in umem_in |
| |
| if addr is not None: |
| self.addr = <void*><uintptr_t>addr |
| self.is_user_addr = True |
| else: |
| self.addr = <void*><uintptr_t>posix_memalign(size, alignment) |
| memset(self.addr, 0, size) |
| self.is_user_addr = False |
| |
| if pgsz_bitmap or comp_mask: |
| umem_in.addr = self.addr |
| umem_in.size = size |
| umem_in.access = access |
| umem_in.pgsz_bitmap = pgsz_bitmap |
| umem_in.comp_mask = comp_mask |
| umem_in.dmabuf_fd = dmabuf_fd |
| self.umem = dv.mlx5dv_devx_umem_reg_ex(context.context, &umem_in) |
| else: |
| self.umem = dv.mlx5dv_devx_umem_reg(context.context, self.addr, |
| size, access) |
| if self.umem == NULL: |
| raise PyverbsRDMAErrno("Failed to register a UMEM.") |
| self.context = context |
| self.context.add_ref(self) |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.umem != NULL: |
| if self.logger: |
| self.logger.debug('Closing Mlx5UMEM') |
| rc = dv.mlx5dv_devx_umem_dereg(self.umem) |
| try: |
| if rc: |
| raise PyverbsError("Failed to dereg UMEM.", rc) |
| finally: |
| if not self.is_user_addr: |
| free(self.addr) |
| self.umem = NULL |
| self.context = None |
| |
| def __str__(self): |
| print_format = '{:20}: {:<20}\n' |
| return print_format.format('umem id', self.umem_id) + \ |
| print_format.format('reg addr', self.umem_addr) |
| |
| @property |
| def umem_id(self): |
| return self.umem.umem_id |
| |
| @property |
| def umem_addr(self): |
| if self.addr: |
| return <uintptr_t><void*>self.addr |
| |
| |
| cdef class Mlx5Cqe64(PyverbsObject): |
| def __init__(self, addr): |
| self.cqe = <dv.mlx5_cqe64*><uintptr_t> addr |
| |
| def dump(self): |
| dump_format = '{:08x} {:08x} {:08x} {:08x}\n' |
| str = '' |
| for i in range(0, 16, 4): |
| str += dump_format.format(be32toh((<uint32_t*>self.cqe)[i]), |
| be32toh((<uint32_t*>self.cqe)[i + 1]), |
| be32toh((<uint32_t*>self.cqe)[i + 2]), |
| be32toh((<uint32_t*>self.cqe)[i + 3])) |
| return str |
| |
| def is_empty(self): |
| for i in range(16): |
| if be32toh((<uint32_t*>self.cqe)[i]) != 0: |
| return False |
| return True |
| |
| @property |
| def owner(self): |
| return dv.mlx5dv_get_cqe_owner(self.cqe) |
| @owner.setter |
| def owner(self, val): |
| dv.mlx5dv_set_cqe_owner(self.cqe, <uint8_t> val) |
| |
| @property |
| def se(self): |
| return dv.mlx5dv_get_cqe_se(self.cqe) |
| |
| @property |
| def format(self): |
| return dv.mlx5dv_get_cqe_format(self.cqe) |
| |
| @property |
| def opcode(self): |
| return dv.mlx5dv_get_cqe_opcode(self.cqe) |
| |
| @property |
| def imm_inval_pkey(self): |
| return be32toh(self.cqe.imm_inval_pkey) |
| |
| @property |
| def wqe_id(self): |
| return be16toh(self.cqe.wqe_id) |
| |
| @property |
| def byte_cnt(self): |
| return be32toh(self.cqe.byte_cnt) |
| |
| @property |
| def timestamp(self): |
| return be64toh(self.cqe.timestamp) |
| |
| @property |
| def wqe_counter(self): |
| return be16toh(self.cqe.wqe_counter) |
| |
| @property |
| def signature(self): |
| return self.cqe.signature |
| |
| @property |
| def op_own(self): |
| return self.cqe.op_own |
| |
| def __str__(self): |
| return (<dv.mlx5_cqe64>((<dv.mlx5_cqe64*>self.cqe)[0])).__str__() |
| |
| cdef class Mlx5DevxMsiVector(PyverbsCM): |
| """ |
| Represents mlx5dv_devx_msi_vector C struct. |
| """ |
| def __init__(self, Context context): |
| super().__init__() |
| self.msi_vector = dv.mlx5dv_devx_alloc_msi_vector(context.context) |
| if self.msi_vector == NULL: |
| raise PyverbsRDMAErrno('Failed to allocate an msi_vector') |
| |
| @property |
| def vector(self): |
| return self.msi_vector.vector |
| |
| @property |
| def fd(self): |
| return self.msi_vector.fd |
| |
| cpdef close(self): |
| if self.msi_vector != NULL: |
| rc = dv.mlx5dv_devx_free_msi_vector(self.msi_vector) |
| if rc: |
| raise PyverbsRDMAError('Failed to free the msi_vector', rc) |
| self.msi_vector = NULL |
| |
| |
| cdef class Mlx5DevxEq(PyverbsCM): |
| """ |
| Represents mlx5dv_devx_eq C struct. |
| """ |
| def __init__(self, Context context, in_, outlen): |
| """ |
| Creates a DevX EQ object. |
| If the object was successfully created, the command's output would be |
| stored as a memoryview in self.out_view. |
| :param in_: Bytes of the obj_create command's input data provided in a |
| device specification format. |
| (Stream of bytes or __bytes__ is implemented) |
| :param outlen: Expected output length in bytes |
| """ |
| super().__init__() |
| in_bytes = bytes(in_) |
| cdef char *in_mailbox = _prepare_devx_inbox(in_bytes) |
| cdef char *out_mailbox = _prepare_devx_outbox(outlen) |
| self.eq = dv.mlx5dv_devx_create_eq(context.context, in_mailbox, |
| len(in_bytes), out_mailbox, outlen) |
| try: |
| if self.eq == NULL: |
| raise PyverbsRDMAErrno('Failed to create async EQ object') |
| self.out_view = memoryview(out_mailbox[:outlen]) |
| status = hex(self.out_view[0]) |
| syndrome = self.out_view[4:8].hex() |
| if status != hex(0): |
| raise PyverbsRDMAError('Failed to create async EQ object with status' |
| f'({status}) and syndrome (0x{syndrome})') |
| finally: |
| free(in_mailbox) |
| free(out_mailbox) |
| self.context = context |
| self.context.add_ref(self) |
| |
| @property |
| def out_view(self): |
| return self.out_view |
| |
| @property |
| def vaddr(self): |
| return <uintptr_t><void*>self.eq.vaddr |
| |
| def __dealloc__(self): |
| self.close() |
| |
| cpdef close(self): |
| if self.eq != NULL: |
| self.logger.debug('Closing Mlx5DevxEq') |
| rc = dv.mlx5dv_devx_destroy_eq(self.eq) |
| if rc: |
| raise PyverbsRDMAError('Failed to destroy a DevX EQ object', rc) |
| self.eq = NULL |
| self.context = None |