| # SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) | |
| # Copyright (c) 2021 Nvidia Inc. All rights reserved. See COPYING file | |
| from libc.stdlib cimport calloc, free | |
| from libc.stdint cimport uint8_t | |
| from libc.string cimport memcpy | |
| import weakref | |
| from .pyverbs_error import PyverbsRDMAError, PyverbsError, PyverbsUserError | |
| from pyverbs.base import PyverbsRDMAErrno | |
| from pyverbs.base cimport close_weakrefs | |
| cimport pyverbs.libibverbs_enums as e | |
| from pyverbs.device cimport Context | |
| from pyverbs.wr cimport RecvWR | |
| from pyverbs.cq cimport CQ, CQEX | |
| from pyverbs.pd cimport PD | |
| from pyverbs.qp cimport QP | |
| cdef class WQInitAttr(PyverbsObject): | |
| def __init__(self, wq_context=None, PD wq_pd=None, wq_cq=None, wq_type=e.IBV_WQT_RQ, | |
| max_wr=100, max_sge=1, comp_mask=0, create_flags=0): | |
| """ | |
| Initializes a WqInitAttr object representing ibv_wq_init_attr struct. | |
| :param wq_context: Associated WQ context | |
| :param wq_pd: PD to be associated with the WQ | |
| :param wq_cq: CQ or CQEX to be associated with the WQ | |
| :param wp_type: The desired WQ type | |
| :param max_wr: Requested max number of outstanding WRs in the WQ | |
| :param max_sge: Requested max number of scatter/gather (s/g) elements per WR in the WQ | |
| :param comp_mask: Identifies valid fields | |
| :param create_flags: Creation flags for the WQ | |
| :return: A WqInitAttr object | |
| """ | |
| super().__init__() | |
| self.attr.wq_context = <void*>(wq_context) if wq_context else NULL | |
| self.attr.wq_type = wq_type | |
| self.attr.max_wr = max_wr | |
| self.attr.max_sge = max_sge | |
| self.pd = wq_pd | |
| self.attr.pd = wq_pd.pd if wq_pd else NULL | |
| self.cq = wq_cq | |
| if wq_cq: | |
| if isinstance(wq_cq, CQ): | |
| self.attr.cq = (<CQ>wq_cq).cq | |
| else: | |
| self.attr.cq = (<CQEX>wq_cq).ibv_cq | |
| else: | |
| self.attr.cq = NULL | |
| self.attr.comp_mask = comp_mask | |
| self.attr.create_flags = create_flags | |
| @property | |
| def wq_type(self): | |
| return self.attr.wq_type | |
| @wq_type.setter | |
| def wq_type(self, val): | |
| self.attr.wq_type = val | |
| @property | |
| def pd(self): | |
| return self.pd | |
| @pd.setter | |
| def pd(self, PD val): | |
| self.pd = val | |
| self.attr.pd = <v.ibv_pd*>val.pd | |
| @property | |
| def cq(self): | |
| return self.cq | |
| @cq.setter | |
| def cq(self, val): | |
| self.cq = val | |
| if isinstance(val, CQ): | |
| self.attr.cq = (<CQ>val).cq | |
| else: | |
| self.attr.cq = (<CQEX>val).ibv_cq | |
| cdef class WQAttr(PyverbsObject): | |
| def __init__(self, attr_mask=0, wq_state=0, curr_wq_state=0, flags=0, flags_mask=0): | |
| """ | |
| Initializes a WQAttr object which represents ibv_wq_attr struct. It | |
| can be used to modify a WQ. | |
| :param attr_mask: Identifies valid fields | |
| :param wq_state: Desired WQ state | |
| :param curr_wq_state: Current WQ state | |
| :param flags: Flags values to modify | |
| :param flags_mask: Which flags to modify | |
| :return: An initialized WQAttr object | |
| """ | |
| super().__init__() | |
| self.attr.attr_mask = attr_mask | |
| self.attr.wq_state = wq_state | |
| self.attr.curr_wq_state = curr_wq_state | |
| self.attr.flags = flags | |
| self.attr.flags_mask = flags_mask | |
| @property | |
| def wq_state(self): | |
| return self.attr.wq_state | |
| @wq_state.setter | |
| def wq_state(self, val): | |
| self.attr.wq_state = val | |
| @property | |
| def attr_mask(self): | |
| return self.attr.attr_mask | |
| @attr_mask.setter | |
| def attr_mask(self, val): | |
| self.attr.attr_mask = val | |
| @property | |
| def curr_wq_state(self): | |
| return self.attr.curr_wq_state | |
| @curr_wq_state.setter | |
| def curr_wq_state(self, val): | |
| self.attr.curr_wq_state = val | |
| @property | |
| def flags(self): | |
| return self.attr.flags | |
| @flags.setter | |
| def flags(self, val): | |
| self.attr.flags = val | |
| @property | |
| def flags_mask(self): | |
| return self.attr.flags_mask | |
| @flags_mask.setter | |
| def flags_mask(self, val): | |
| self.attr.flags_mask = val | |
| cdef class WQ(PyverbsCM): | |
| def __init__(self, Context ctx, WQInitAttr attr): | |
| """ | |
| Creates a WQ object. | |
| :param ctx: The context the wq will be associated with. | |
| :param attr: WQ initial attributes of type WQInitAttr. | |
| :return: A WQ object | |
| """ | |
| super().__init__() | |
| self.wq = v.ibv_create_wq(ctx.context, &attr.attr) | |
| if self.wq == NULL: | |
| raise PyverbsRDMAErrno('Failed to create WQ') | |
| self.context = ctx | |
| ctx.add_ref(self) | |
| pd = <PD>attr.pd | |
| pd.add_ref(self) | |
| self.pd = pd | |
| if isinstance(attr.cq, CQ): | |
| (<CQ>attr.cq).add_ref(self) | |
| elif isinstance(attr.cq, CQEX): | |
| (<CQEX>attr.cq).add_ref(self) | |
| self.cq = attr.cq | |
| self.rwq_ind_tables = weakref.WeakSet() | |
| cpdef add_ref(self, obj): | |
| if isinstance(obj, RwqIndTable): | |
| self.rwq_ind_tables.add(obj) | |
| else: | |
| raise PyverbsError('Unrecognized object type') | |
| def modify(self, WQAttr wq_attr not None): | |
| """ | |
| Modify the WQ | |
| :param qp_attr: A WQAttr object with updated values to be applied to | |
| the WQ | |
| :return: None | |
| """ | |
| rc = v.ibv_modify_wq(self.wq, &wq_attr.attr) | |
| if rc != 0: | |
| raise PyverbsRDMAError('Failed to modify WQ', rc) | |
| def post_recv(self, RecvWR wr not None, RecvWR bad_wr=None): | |
| """ | |
| Post a receive WR on the WQ. | |
| :param wr: The work request to post | |
| :param bad_wr: A RecvWR object to hold the bad WR if it is available in | |
| case of a failure | |
| :return: None | |
| """ | |
| cdef v.ibv_recv_wr *my_bad_wr | |
| # In order to provide a pointer to a pointer, use a temporary cdef'ed | |
| # variable. | |
| rc = v.ibv_post_wq_recv(self.wq, &wr.recv_wr, &my_bad_wr) | |
| if rc != 0: | |
| if (bad_wr): | |
| memcpy(&bad_wr.recv_wr, my_bad_wr, sizeof(bad_wr.recv_wr)) | |
| raise PyverbsRDMAError('Failed to post recv', rc) | |
| def __dealloc__(self): | |
| self.close() | |
| cpdef close(self): | |
| """ | |
| Closes the underlying C object of the WQ. | |
| :return: None | |
| """ | |
| if self.wq != NULL: | |
| if self.logger: | |
| self.logger.debug('Closing WQ') | |
| close_weakrefs([self.rwq_ind_tables]) | |
| rc = v.ibv_destroy_wq(self.wq) | |
| if rc != 0: | |
| raise PyverbsRDMAError('Failed to dealloc WQ', rc) | |
| self.wq = NULL | |
| self.context = None | |
| self.pd = None | |
| self.cq = None | |
| @property | |
| def wqn(self): | |
| return self.wq.wq_num | |
| cdef class RwqIndTableInitAttr(PyverbsObject): | |
| def __init__(self, log_ind_tbl_size=5, wqs_list=None, comp_mask=0): | |
| """ | |
| Initializes a RwqIndTableInitAttr object representing ibv_rwq_ind_table_init_attr struct. | |
| :param log_ind_tbl_size: Log, base 2, of Indirection table size | |
| :param wqs_list: List of WQs | |
| :param comp_mask: Identifies valid fields | |
| :return: A RwqIndTableInitAttr object | |
| """ | |
| super().__init__() | |
| if log_ind_tbl_size <= 0: | |
| raise PyverbsUserError('Invalid indirection table size. Log size must be > 0') | |
| if (1 << log_ind_tbl_size) < len(wqs_list): | |
| raise PyverbsUserError(f'Requested table size ({1 << log_ind_tbl_size}) is smaller ' | |
| f'than the number of wqs ({len(wqs_list)})') | |
| self.attr.log_ind_tbl_size = log_ind_tbl_size | |
| cdef v.ibv_wq **rwq_ind_table = <v.ibv_wq **>calloc(len(wqs_list), sizeof(v.ibv_wq*)) | |
| if rwq_ind_table == NULL: | |
| raise MemoryError('Failed to allocate memory for Indirection Table') | |
| for i in range(len(wqs_list)): | |
| rwq_ind_table[i] = (<WQ>wqs_list[i]).wq | |
| self.attr.ind_tbl = rwq_ind_table | |
| self.wqs_list = wqs_list | |
| self.attr.comp_mask = comp_mask | |
| def __dealloc__(self): | |
| """ | |
| Closes the rwq_ind_tbl init attr. | |
| :return: None | |
| """ | |
| free(self.attr.ind_tbl) | |
| cdef class RwqIndTable(PyverbsCM): | |
| def __init__(self, Context ctx, RwqIndTableInitAttr attr): | |
| """ | |
| Initializes a RwqIndTable object. | |
| :param ctx: The context the RWQ IND TBL will be associated with. | |
| :param attr: RWQ IND TBL initial attributes of type RwqIndTableInitAttr. | |
| :return: A RwqIndTable object | |
| """ | |
| super().__init__() | |
| self.rwq_ind_table = v.ibv_create_rwq_ind_table(ctx.context, &attr.attr) | |
| if self.rwq_ind_table == NULL: | |
| raise PyverbsRDMAErrno('Failed to create RwqIndTable') | |
| self.context = ctx | |
| ctx.add_ref(self) | |
| self.wqs = attr.wqs_list | |
| for wq in self.wqs: | |
| wq.add_ref(self) | |
| self.qps = weakref.WeakSet() | |
| cpdef add_ref(self, obj): | |
| if isinstance(obj, QP): | |
| self.qps.add(obj) | |
| else: | |
| raise PyverbsError('Unrecognized object type') | |
| @property | |
| def wqs(self): | |
| return self.wqs | |
| def __dealloc__(self): | |
| self.close() | |
| cpdef close(self): | |
| """ | |
| Closes the underlying C object of the RWQ IND TBL. | |
| :return: None | |
| """ | |
| if self.rwq_ind_table != NULL: | |
| if self.logger: | |
| self.logger.debug('Closing RWQ IND TBL') | |
| close_weakrefs([self.qps]) | |
| rc = v.ibv_destroy_rwq_ind_table(self.rwq_ind_table) | |
| if rc != 0: | |
| raise PyverbsRDMAError('Failed to dealloc RWQ IND TBL', rc) | |
| self.rwq_ind_table = NULL | |
| self.context = None | |
| cdef class RxHashConf(PyverbsObject): | |
| def __init__(self, rx_hash_function=0, rx_hash_key_len=0, | |
| rx_hash_key=None, rx_hash_fields_mask=0): | |
| """ | |
| Initializes a RxHashConf object representing ibv_rx_hash_conf struct. | |
| :param rx_hash_function: RX hash function, use enum ibv_rx_hash_function_flags | |
| :param rx_hash_key_len: RX hash key length | |
| :param rx_hash_key: RX hash key data | |
| :param rx_hash_fields_mask: RX fields that should participate in the hashing | |
| :return: A RxHashConf object | |
| """ | |
| super().__init__() | |
| cdef uint8_t *rx_hash_key_c = NULL | |
| if rx_hash_key: | |
| if rx_hash_key_len != len(rx_hash_key): | |
| raise PyverbsUserError('Length of rx_hash_key not equal to rx_hash_key_len') | |
| self.rx_hash_key = rx_hash_key | |
| self.rx_hash_conf.rx_hash_function = rx_hash_function | |
| self.rx_hash_conf.rx_hash_key_len = rx_hash_key_len | |
| self.rx_hash_conf.rx_hash_fields_mask = rx_hash_fields_mask | |
| @property | |
| def rx_hash_function(self): | |
| return self.rx_hash_conf.rx_hash_function | |
| @rx_hash_function.setter | |
| def rx_hash_function(self, val): | |
| self.rx_hash_conf.rx_hash_function = val | |
| @property | |
| def rx_hash_key_len(self): | |
| return self.rx_hash_conf.rx_hash_key_len | |
| @rx_hash_key_len.setter | |
| def rx_hash_key_len(self, val): | |
| if val <= 0: | |
| raise PyverbsUserError('Invalid rx_hash_key_len. Must be greater then 0') | |
| self.rx_hash_conf.rx_hash_key_len = val | |
| @property | |
| def rx_hash_fields_mask(self): | |
| return self.rx_hash_conf.rx_hash_fields_mask | |
| @rx_hash_fields_mask.setter | |
| def rx_hash_fields_mask(self, val): | |
| self.rx_hash_conf.rx_hash_fields_mask = val | |
| @property | |
| def rx_hash_key(self): | |
| return self.rx_hash_conf.rx_hash_fields_mask | |
| @rx_hash_key.setter | |
| def rx_hash_key(self, vals_list): | |
| if self.rx_hash_conf.rx_hash_key != NULL: | |
| free(self.rx_hash_conf.rx_hash_key) | |
| self.rx_hash_conf.rx_hash_key = NULL | |
| cdef uint8_t *rx_hash_key_c = <uint8_t*>calloc(len(vals_list), sizeof(uint8_t)) | |
| if rx_hash_key_c == NULL: | |
| raise MemoryError('Failed to allocate memory for RX hash key') | |
| for i in range(len(vals_list)): | |
| rx_hash_key_c[i] = vals_list[i] | |
| self.rx_hash_conf.rx_hash_key = rx_hash_key_c | |
| self.rx_hash_conf.rx_hash_key_len = len(vals_list) | |
| def __dealloc__(self): | |
| """ | |
| Frees rx hash key allocated memory. | |
| :return: None | |
| """ | |
| free(self.rx_hash_conf.rx_hash_key) | |
| self.rx_hash_conf.rx_hash_key = NULL |