blob: deada022bd1c18334a634f0e5f2cac6e617a588c [file] [log] [blame] [edit]
# 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