/
githubmirror
/
rdma-core
Обзор
Документация
Войти
/
githubmirror
/
rdma-core
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
pyverbs/mr.pyx
826 строк
30 KB
Jiri Pirko
pyverbs: Add DMA-buf fd export for Buf
14 июл 2026, 17:41
14 июл 2026, 17:41
7f18b0e
Код
Авторство
О чём код?
# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) # Copyright (c) 2019, Mellanox Technologies. All rights reserved. See COPYING file # Copyright (c) 2020, Intel Corporation. All rights reserved. See COPYING file import resource import logging import weakref from posix.mman cimport mmap, munmap, MAP_PRIVATE, PROT_READ, PROT_WRITE, \ MAP_ANONYMOUS, MAP_HUGETLB, MAP_SHARED from pyverbs.pyverbs_error import PyverbsError, PyverbsRDMAError, \ PyverbsUserError from libc.stdint cimport uintptr_t, SIZE_MAX from pyverbs.utils import rereg_error_to_str from pyverbs.base import PyverbsRDMAErrno from posix.stdlib cimport posix_memalign from libc.string cimport memcpy, memset cimport pyverbs.libibverbs_enums as e from pyverbs.device cimport DM from libc.stdlib cimport free, malloc from pyverbs.device cimport Context from .cmid cimport CMID from .pd cimport PD from .dmabuf cimport DmaBuf from pyverbs.base cimport close_weakrefs cdef extern from 'sys/mman.h': cdef void* MAP_FAILED HUGE_PAGE_SIZE = 0x200000 cdef class MR(PyverbsCM): """ MR class represents ibv_mr. Buffer allocation in done in the c'tor. Freeing it is done in close(). """ def __init__(self, creator not None, length=0, access=0, address=None, implicit=False, **kwargs): """ Allocate a user-level buffer of length <length> and register a Memory Region of the given length and access flags. :param creator: A PD/CMID object. In case of CMID is passed the MR will be registered using rdma_reg_msgs/write/read according to the passed access flag of local_write/remote_write or remote_read respectively. :param length: Length (in bytes) of MR's buffer. :param access: Access flags, see ibv_access_flags enum :param address: Memory address to register (Optional). If it's not provided, a memory will be allocated in the class initialization. :param implicit: Implicit the MR address. :param kwargs: Arguments: * *handle* A valid kernel handle for a MR object in the given PD (creator). If passed, the MR will be imported and associated with the context that is associated with the given PD using ibv_import_mr. :return: The newly created MR on success """ super().__init__() cdef int mmap_len = 0 if self.mr != NULL: return self.is_huge = True if access & e.IBV_ACCESS_HUGETLB else False if address: self.is_user_addr = True # uintptr_t is guaranteed to be large enough to hold any pointer. # In order to safely cast addr to void*, it is firstly cast to uintptr_t. self.buf = <void*><uintptr_t>address mr_handle = kwargs.get('handle') # If a MR handle is passed import MR and finish if mr_handle is not None: pd = <PD>creator self.mr = v.ibv_import_mr(pd.pd, mr_handle) if self.mr == NULL: raise PyverbsRDMAErrno('Failed to import MR') self._is_imported = True self.pd = pd pd.add_ref(self) return # Allocate a buffer if not address and length > 0: self._allocate_buffer(<size_t>length, self.is_huge, &mmap_len) if self.buf == NULL: raise PyverbsError('Failed to allocate MR buffer of size {l}'. format(l=length)) self.mmap_length = mmap_len if isinstance(creator, PD): pd = <PD>creator if implicit: self.mr = v.ibv_reg_mr(pd.pd, NULL, SIZE_MAX, access) else: self.mr = v.ibv_reg_mr(pd.pd, self.buf, length, access) self.pd = pd pd.add_ref(self) elif isinstance(creator, CMID): cmid = <CMID>creator if access == e.IBV_ACCESS_LOCAL_WRITE: self.mr = cm.rdma_reg_msgs(cmid.id, self.buf, length) elif access == e.IBV_ACCESS_REMOTE_WRITE: self.mr = cm.rdma_reg_write(cmid.id, self.buf, length) elif access == e.IBV_ACCESS_REMOTE_READ: self.mr = cm.rdma_reg_read(cmid.id, self.buf, length) self.cmid = cmid cmid.add_ref(self) if self.mr == NULL: raise PyverbsRDMAErrno('Failed to register a MR. length: {l}, access flags: {a}'. format(l=length, a=access)) self.logger.debug('Registered ibv_mr. Length: {l}, access flags {a}'. format(l=length, a=access)) def unimport(self): v.ibv_unimport_mr(self.mr) self.close() def __dealloc__(self): self.close() cpdef close(self): """ Closes the underlying C object of the MR and frees the memory allocated. MR may be deleted directly or indirectly by closing its context, which leaves the Python PD object without the underlying C object, so during destruction, need to check whether or not the C object exists. In case of an imported MR no deregistration will be done, it's left for the original MR, in order to prevent double dereg by the GC. :return: None """ if self.mr != NULL: if self.logger: self.logger.debug('Closing MR') if not self._is_imported: rc = v.ibv_dereg_mr(self.mr) if rc != 0: raise PyverbsRDMAError('Failed to dereg MR', rc) if not self.is_user_addr: self._free_buffer(self.is_huge, self.mmap_length) self.mr = NULL self.pd = None self.buf = NULL self.cmid = None def write(self, data, length, offset=0): """ Write user data to the MR's buffer using memcpy :param data: User data to write :param length: Length of the data to write :param offset: Writing offset :return: None """ if not self.buf or length < 0: raise PyverbsUserError('The MR buffer isn\'t allocated or length' f' {length} is invalid') # If data is a string, cast it to bytes as Python3 doesn't # automatically convert it. cdef int off = offset if isinstance(data, str): data = data.encode() memcpy(<char*>(self.buf + off), <char *>data, length) cpdef read(self, length, offset): """ Reads data from the MR's buffer :param length: Length of data to read :param offset: Reading offset :return: The data on the buffer in the requested offset """ cdef char *data cdef int off = offset # we can't use offset in the next line, as it is # a Python object and not C if offset < 0: raise PyverbsUserError(f'Invalid offset {offset}') if not self.buf or length < 0: raise PyverbsUserError('The MR buffer isn\'t allocated or length' f' {length} is invalid') data = <char*>(self.buf + off) return data[:length] def rereg(self, flags, PD pd=None, addr=0, length=0, access=0): """ Modifies the attributes of an existing memory region. :param flags: Bit-mask used to indicate which of the properties of the MR are being modified :param pd: New PD :param addr: New addr to reg the MR on :param length: New length of memory to reg :param access: New MR access :return: None """ ret = v.ibv_rereg_mr(self.mr, flags, pd.pd, <void*><uintptr_t>addr, length, access) if ret != 0: err_msg = rereg_error_to_str(ret) raise PyverbsRDMAErrno(f'Failed to rereg MR: {err_msg}') if flags & e.IBV_REREG_MR_CHANGE_TRANSLATION: if not self.is_user_addr: self._free_buffer(self.is_huge, self.mmap_length) self.buf = <void*><uintptr_t>addr self.is_user_addr = True if flags & e.IBV_REREG_MR_CHANGE_PD: (<PD>self.pd).remove_ref(self) self.pd = pd pd.add_ref(self) cdef void _allocate_buffer(self, size_t length, bint is_huge, int *mmap_length): cdef void *buf = NULL cdef size_t rounded cdef int rc if length == 0: mmap_length[0] = 0 return if is_huge: rounded = length + (HUGE_PAGE_SIZE - length % HUGE_PAGE_SIZE) if \ length % HUGE_PAGE_SIZE else length buf = mmap(NULL, rounded, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0) if buf == MAP_FAILED: return mmap_length[0] = <int>rounded else: rc = posix_memalign(&buf, resource.getpagesize(), length) if rc: return mmap_length[0] = 0 memset(buf, 0, length) self.buf = buf cdef void _free_buffer(self, bint is_huge, int mmap_length): if self.buf == NULL: return if is_huge: munmap(self.buf, mmap_length) else: free(self.buf) @property def buf(self): return <uintptr_t>self.buf @property def lkey(self): return self.mr.lkey @property def rkey(self): return self.mr.rkey @property def length(self): return self.mr.length @property def handle(self): return self.mr.handle def __str__(self): print_format = '{:22}: {:<20}\n' return 'MR:\n' + \ print_format.format('lkey', self.lkey) + \ print_format.format('rkey', self.rkey) + \ print_format.format('length', self.length) + \ print_format.format('buf', <uintptr_t>self.buf) + \ print_format.format('handle', self.handle) cdef class MWBindInfo(PyverbsCM): def __init__(self, MR mr not None, addr, length, mw_access_flags): super().__init__() self.mr = mr self.info.mr = mr.mr self.info.addr = addr self.info.length = length self.info.mw_access_flags = mw_access_flags @property def mw_access_flags(self): return self.info.mw_access_flags @property def length(self): return self.info.length @property def addr(self): return self.info.addr def __str__(self): print_format = '{:22}: {:<20}\n' return 'MWBindInfo:\n' +\ print_format.format('Addr', self.info.addr) +\ print_format.format('Length', self.info.length) +\ print_format.format('MW access flags', self.info.mw_access_flags) cdef class MWBind(PyverbsCM): def __init__(self, MWBindInfo info not None,send_flags, wr_id=0): super().__init__() self.mw_bind.wr_id = wr_id self.mw_bind.send_flags = send_flags self.mw_bind.bind_info = info.info def __str__(self): print_format = '{:22}: {:<20}\n' return 'MWBind:\n' +\ print_format.format('WR id', self.mw_bind.wr_id) +\ print_format.format('Send flags', self.mw_bind.send_flags) cdef class MW(PyverbsCM): def __init__(self, PD pd not None, v.ibv_mw_type mw_type): """ Initializes a memory window object of the given type :param pd: A PD object :param mw_type: Type of of the memory window, see ibv_mw_type enum :return: """ super().__init__() self.mw = NULL self.mw = v.ibv_alloc_mw(pd.pd, mw_type) if self.mw == NULL: raise PyverbsRDMAErrno('Failed to allocate MW') self.pd = pd pd.add_ref(self) self.logger.debug('Allocated memory window of type {t}'. format(t=mwtype2str(mw_type))) def __dealloc__(self): self.close() cpdef close(self): """ Closes the underlying C MW object. MW may be deleted directly or by deleting its PD, which leaves the Python object without the underlying MW. Need to check that the underlying MW wasn't dealloced before. :return: None """ if self.mw is not NULL: if self.logger: self.logger.debug('Closing MW') rc = v.ibv_dealloc_mw(self.mw) if rc != 0: raise PyverbsRDMAError('Failed to dealloc MW', rc) self.mw = NULL self.pd = None @property def handle(self): return self.mw.handle @property def rkey(self): return self.mw.rkey @property def type(self): return self.mw.type def __str__(self): print_format = '{:22}: {:<20}\n' return 'MW:\n' +\ print_format.format('Rkey', self.mw.rkey) +\ print_format.format('Handle', self.mw.handle) +\ print_format.format('MW Type', mwtype2str(self.mw.type)) cdef class DMMR(MR): def __init__(self, PD pd not None, length, access, DM dm, offset): """ Initializes a DMMR (Device Memory Memory Region) of the given length and access flags using the given PD and DM objects. :param pd: A PD object :param length: Length in bytes :param access: Access flags, see ibv_access_flags enum :param dm: A DM (device memory) object to be used for this DMMR :param offset: Byte offset from the beginning of the allocated device memory buffer :return: The newly create DMMR """ # Initialize the logger here as the parent's __init__ is called after # the DMMR is allocated. Allocation can fail, which will lead to # exceptions thrown during object's teardown. self.logger = logging.getLogger(self.__class__.__name__) self.mr = v.ibv_reg_dm_mr(pd.pd, dm.dm, offset, length, access) if self.mr == NULL: raise PyverbsRDMAErrno('Failed to register a device MR. length: {len}, access flags: {flags}'. format(len=length, flags=access,)) super().__init__(pd, length, access) self.pd = pd self.dm = dm pd.add_ref(self) dm.add_ref(self) self.logger.debug('Registered device ibv_mr. Length: {len}, access flags {flags}'. format(len=length, flags=access)) def write(self, data, length, offset=0): if isinstance(data, str): data = data.encode() return self.dm.copy_to_dm(offset, data, length) cpdef read(self, length, offset): return self.dm.copy_from_dm(offset, length) cdef class DmaBufMR(MR): def __init__(self, PD pd not None, length, access, dmabuf=None, offset=0, gpu=0, gtt=0): """ Initializes a DmaBufMR (DMA-BUF Memory Region) of the given length and access flags using the given PD and DmaBuf objects. :param pd: A PD object :param length: Length in bytes :param access: Access flags, see ibv_access_flags enum :param dmabuf: A DmaBuf object or a FD representing a dmabuf. DmaBuf object will be allocated if None is passed. :param offset: Byte offset from the beginning of the dma-buf :param gpu: GPU unit for internal dmabuf allocation :param gtt: If true allocate internal dmabuf from GTT instead of VRAM :return: The newly created DMABUFMR """ self.logger = logging.getLogger(self.__class__.__name__) if dmabuf is None: self.is_dmabuf_internal = True dmabuf = DmaBuf(length + offset, gpu, gtt) fd = dmabuf.fd if isinstance(dmabuf, DmaBuf) else dmabuf self.mr = v.ibv_reg_dmabuf_mr(pd.pd, offset, length, offset, fd, access) if self.mr == NULL: raise PyverbsRDMAErrno(f'Failed to register a dma-buf MR. length: {length}, access flags: {access}') super().__init__(pd, length, access) self.pd = pd self.dmabuf = dmabuf self.offset = offset pd.add_ref(self) if isinstance(dmabuf, DmaBuf): dmabuf.add_ref(self) self.logger.debug(f'Registered dma-buf ibv_mr. Length: {length}, access flags {access}') def __dealloc__(self): self.close() cpdef close(self): """ Closes the underlying C object of the MR and frees the memory allocated. :return: None """ if self.mr != NULL: if self.logger: self.logger.debug('Closing dma-buf MR') rc = v.ibv_dereg_mr(self.mr) if rc != 0: raise PyverbsRDMAError('Failed to dereg dma-buf MR', rc) self.pd = None self.mr = NULL # Set self.mr to NULL before closing dmabuf because this method is # re-entered when close_weakrefs() is called inside dmabuf.close(). if self.is_dmabuf_internal: self.dmabuf.close() self.dmabuf = None @property def offset(self): return self.offset @property def dmabuf(self): return self.dmabuf def write(self, data, length, offset=0): """ Write user data to the dma-buf backing the MR :param data: User data to write :param length: Length of the data to write :param offset: Writing offset :return: None """ if isinstance(data, str): data = data.encode() cdef int off = offset + self.offset cdef void *buf = mmap(NULL, length + off, PROT_READ | PROT_WRITE, MAP_SHARED, self.dmabuf.drm_fd, self.dmabuf.map_offset) if buf == MAP_FAILED: raise PyverbsError(f'Failed to map dma-buf of size {length}') memcpy(<char*>(buf + off), <char *>data, length) munmap(buf, length + off) cpdef read(self, length, offset): """ Reads data from the dma-buf backing the MR :param length: Length of data to read :param offset: Reading offset :return: The data on the buffer in the requested offset """ cdef int off = offset + self.offset cdef void *buf = mmap(NULL, length + off, PROT_READ | PROT_WRITE, MAP_SHARED, self.dmabuf.drm_fd, self.dmabuf.map_offset) if buf == MAP_FAILED: raise PyverbsError(f'Failed to map dma-buf of size {length}') cdef char *data =<char*>malloc(length) memset(data, 0, length) memcpy(data, <char*>(buf + off), length) munmap(buf, length + off) res = data[:length] free(data) return res cdef class DmaHandleInitAttr(PyverbsObject): def __init__(self, comp_mask=0, cpu_id=0, ph=0, tph_mem_type=0): """ Initialize a DmaHandleInitAttr object with the specified configuration. :param comp_mask: Bitmask of initialization attributes :param cpu_id: CPU identifier for DMA operations :param ph: Processing hints for DMA operations :param tph_mem_type: Type of target memory """ super().__init__() self.init_attr.comp_mask = comp_mask self.init_attr.cpu_id = cpu_id self.init_attr.ph = ph self.init_attr.tph_mem_type = tph_mem_type @property def comp_mask(self): return self.init_attr.comp_mask @comp_mask.setter def comp_mask(self, value): self.init_attr.comp_mask = value @property def cpu_id(self): return self.init_attr.cpu_id @cpu_id.setter def cpu_id(self, value): self.init_attr.cpu_id = value @property def ph(self): return self.init_attr.ph @ph.setter def ph(self, value): self.init_attr.ph = value @property def tph_mem_type(self): return self.init_attr.tph_mem_type @tph_mem_type.setter def tph_mem_type(self, value): self.init_attr.tph_mem_type = value def __str__(self): print_format = '{:22}: {:<20}\n' return 'DmaHandleInitAttr:\n' + \ print_format.format('comp_mask', self.comp_mask) + \ print_format.format('cpu_id', self.cpu_id) + \ print_format.format('ph', self.ph) + \ print_format.format('tph_mem_type', self.tph_mem_type) cdef class DMAHandle(PyverbsCM): def __init__(self, Context ctx not None, DmaHandleInitAttr init_attr not None): """ Initialize a DMAHandle object with the specified configuration. :param ctx: A Context object representing the RDMA device context :param init_attr: A DmaHandleInitAttr object containing initialization attributes :raises PyverbsError: If DMA handle allocation fails """ super().__init__() self.dmah = v.ibv_alloc_dmah(ctx.context, &init_attr.init_attr) if self.dmah == NULL: raise PyverbsRDMAErrno('Failed to create DMA Handle') self.mrs = weakref.WeakSet() self.ctx = ctx ctx.add_ref(self) def __dealloc__(self): self.close() cpdef close(self): """ Close and deallocate the DMA handle. :raises PyverbsRDMAError: If deallocation fails """ if self.dmah != NULL: if self.logger: self.logger.debug('Closing DMA Handle') close_weakrefs([self.mrs]) rc = v.ibv_dealloc_dmah(self.dmah) if rc != 0: raise PyverbsRDMAError('Failed to dealloc DMA Handle', rc) self.dmah = NULL self.ctx = None cdef add_ref(self, obj): if isinstance(obj, MREx): self.mrs.add(obj) else: raise PyverbsError('Unrecognized object type') def mwtype2str(mw_type): mw_types = {1:'IBV_MW_TYPE_1', 2:'IBV_MW_TYPE_2'} try: return mw_types[mw_type] except KeyError: return 'Unknown MW type ({t})'.format(t=mw_type) cdef class MREx(MR): """ MREx class represents a memory region registered using the extended API ibv_reg_mr_ex. This class provides more flexibility in memory registration compared to the basic MR class. """ def __init__(self, PD pd not None, length=0, access=0, address=None, iova=None, fd=None, fd_offset=0, dmah=None, implicit=False, Buf buf=None, **kwargs): """ Register a memory region using the extended API ibv_reg_mr_ex. :param pd: A PD object :param length: Length (in bytes) of MR's buffer :param access: Access flags, see ibv_access_flags enum :param address: Memory address to register; defaults to the start of buf when buf is given (Optional) :param iova: IOVA address to register (Optional) :param fd: File descriptor for dma-buf based registration (Optional) :param fd_offset: Offset in the dma-buf (Optional) :param dmah: DMA handle for registration (Optional) :param implicit: If True, register implicit MR :param buf: A Buf object to register the MR on (Optional). :param kwargs: Additional arguments :return: The newly created MREx on success """ PyverbsCM.__init__(self) cdef int mmap_len = 0 cdef v.ibv_mr_init_attr in_ self.is_huge = True if access & e.IBV_ACCESS_HUGETLB else False self.is_user_addr = False self.mmap_length = 0 if buf is not None: self.is_user_addr = True if address is None: self.buf = buf.addr else: self.buf = <void*><uintptr_t>address # Handle memory allocation if no address is provided elif not address and length > 0 and fd is None: self._allocate_buffer(<size_t>length, self.is_huge, &mmap_len) if self.buf == NULL: raise PyverbsError(f'Failed to allocate MR buffer of size {length}') self.mmap_length = mmap_len elif address: self.is_user_addr = True self.buf = <void*><uintptr_t>address memset(&in_, 0, sizeof(in_)) if implicit: in_.length = SIZE_MAX in_.comp_mask |= e.IBV_REG_MR_MASK_ADDR in_.addr = NULL else: in_.length = length if self.buf != NULL: in_.comp_mask |= e.IBV_REG_MR_MASK_ADDR in_.addr = self.buf in_.access = access if buf is not None: in_.comp_mask |= e.IBV_REG_MR_MASK_BUF in_.buf = buf.bufh if iova is not None: in_.comp_mask |= e.IBV_REG_MR_MASK_IOVA in_.iova = iova if fd is not None: in_.comp_mask |= e.IBV_REG_MR_MASK_FD | e.IBV_REG_MR_MASK_FD_OFFSET in_.fd = fd in_.fd_offset = fd_offset if dmah is not None: in_.comp_mask |= e.IBV_REG_MR_MASK_DMAH in_.dmah = (<DMAHandle>dmah).dmah self.mr = v.ibv_reg_mr_ex(pd.pd, &in_) if self.mr == NULL: # Clean up allocated memory if registration fails if not self.is_user_addr and self.buf != NULL: self._free_buffer(self.is_huge, self.mmap_length) raise PyverbsRDMAErrno('Failed to register MR using extended API') self.pd = pd pd.add_ref(self) if buf is not None: self.backing_buf = buf buf.add_ref(self) if dmah is not None: (<DMAHandle>dmah).add_ref(self) self.dmah = dmah self.logger.debug(f'Registered MR using extended API. Length: {length}, ' f'access flags {access}') def __str__(self): print_format = '{:22}: {:<20}\n' return 'MREx:\n' + \ print_format.format('lkey', self.lkey) + \ print_format.format('rkey', self.rkey) + \ print_format.format('length', self.length) + \ print_format.format('buf', <uintptr_t>self.buf) + \ print_format.format('handle', self.handle) cdef class Buf(PyverbsCM): """ Represents an ibv_buf buffer allocated through a PD. The device provider selects the backing memory for the given PD. """ def __init__(self, PD pd not None, size): """ Allocate a buffer of the given size from the provider associated with the given protection domain (or parent domain). :param pd: A PD/ParentDomain object used for the allocation :param size: Size (in bytes) of the buffer to allocate :return: The newly created Buf on success """ super().__init__() self.mrs = weakref.WeakSet() self.addr = v.ibv_alloc_buf(pd.pd, size, &self.bufh) if self.addr == NULL: raise PyverbsRDMAErrno(f'Failed to allocate ibv_buf of size {size}') self.size = size self.pd = pd pd.add_ref(self) self.logger.debug(f'Allocated ibv_buf of size {size}') def __dealloc__(self): self.close() cpdef close(self): """ Frees the underlying buffer using ibv_free_buf(). :return: None """ if self.bufh != NULL: if self.logger: self.logger.debug('Closing Buf') close_weakrefs([self.mrs]) v.ibv_free_buf(self.bufh) self.bufh = NULL self.addr = NULL self.pd = None cdef add_ref(self, obj): if isinstance(obj, (BufMR, MREx)): self.mrs.add(obj) else: raise PyverbsError('Unrecognized object type') def export_dmabuf_fd(self): """ Export a dmabuf FD for this Buf object. :return: A file descriptor (int) for the dmabuf FD """ fd = v.ibv_export_buf_dmabuf_fd(self.bufh) if fd < 0: raise PyverbsRDMAErrno('Failed to export dmabuf FD for Buf') return fd @property def addr(self): return <uintptr_t>self.addr @property def size(self): return self.size def __str__(self): print_format = '{:22}: {:<20}\n' return 'Buf:\n' + \ print_format.format('addr', <uintptr_t>self.addr) + \ print_format.format('size', self.size) cdef class BufMR(MR): """ BufMR represents a memory region registered for a Buf via ibv_reg_buf_mr(). Unlike MR, the backing memory is owned by the Buf, so closing a BufMR only deregisters the MR and never frees the buffer. The IBV_REG_MR_MASK_BUF path of ibv_reg_mr_ex() is exercised through the MREx class instead. """ def __init__(self, PD pd not None, Buf buf not None, length=0, access=0, offset=0): """ Register a memory region for (a subrange of) the given Buf. :param pd: The same PD/ParentDomain used to allocate the Buf :param buf: A Buf object allocated with ibv_alloc_buf() :param length: Length (in bytes) to register :param access: Access flags, see ibv_access_flags enum :param offset: Byte offset within the Buf to start the registration :return: The newly created BufMR on success """ self.logger = logging.getLogger(self.__class__.__name__) cdef void *addr = <void*>(<uintptr_t>buf.addr + <uintptr_t>offset) self.mr = v.ibv_reg_buf_mr(pd.pd, buf.bufh, addr, length, access) if self.mr == NULL: raise PyverbsRDMAErrno(f'Failed to register a buf MR. length: ' f'{length}, access flags: {access}') self.buf = addr super().__init__(pd, length, access) self.is_user_addr = True self.is_huge = False self.mmap_length = 0 self.pd = pd self.backing_buf = buf pd.add_ref(self) buf.add_ref(self) self.logger.debug(f'Registered buf ibv_mr. Length: {length}, access ' f'flags {access}')