|  | /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ | 
|  | /* | 
|  | * Copyright (c) 2007 Cisco Systems.  All rights reserved. | 
|  | * Copyright (c) 2020 Intel Corporation.  All rights reserved. | 
|  | */ | 
|  |  | 
|  | #ifndef IB_UMEM_H | 
|  | #define IB_UMEM_H | 
|  |  | 
|  | #include <linux/list.h> | 
|  | #include <linux/scatterlist.h> | 
|  | #include <linux/workqueue.h> | 
|  | #include <rdma/ib_verbs.h> | 
|  |  | 
|  | struct ib_ucontext; | 
|  | struct ib_umem_odp; | 
|  | struct dma_buf_attach_ops; | 
|  |  | 
|  | struct ib_umem { | 
|  | struct ib_device       *ibdev; | 
|  | struct mm_struct       *owning_mm; | 
|  | u64 iova; | 
|  | size_t			length; | 
|  | unsigned long		address; | 
|  | u32 writable : 1; | 
|  | u32 is_odp : 1; | 
|  | u32 is_dmabuf : 1; | 
|  | struct sg_append_table sgt_append; | 
|  | }; | 
|  |  | 
|  | struct ib_umem_dmabuf { | 
|  | struct ib_umem umem; | 
|  | struct dma_buf_attachment *attach; | 
|  | struct sg_table *sgt; | 
|  | struct scatterlist *first_sg; | 
|  | struct scatterlist *last_sg; | 
|  | unsigned long first_sg_offset; | 
|  | unsigned long last_sg_trim; | 
|  | void *private; | 
|  | u8 pinned : 1; | 
|  | u8 revoked : 1; | 
|  | }; | 
|  |  | 
|  | static inline struct ib_umem_dmabuf *to_ib_umem_dmabuf(struct ib_umem *umem) | 
|  | { | 
|  | return container_of(umem, struct ib_umem_dmabuf, umem); | 
|  | } | 
|  |  | 
|  | /* Returns the offset of the umem start relative to the first page. */ | 
|  | static inline int ib_umem_offset(struct ib_umem *umem) | 
|  | { | 
|  | return umem->address & ~PAGE_MASK; | 
|  | } | 
|  |  | 
|  | static inline dma_addr_t ib_umem_start_dma_addr(struct ib_umem *umem) | 
|  | { | 
|  | return sg_dma_address(umem->sgt_append.sgt.sgl) + ib_umem_offset(umem); | 
|  | } | 
|  |  | 
|  | static inline unsigned long ib_umem_dma_offset(struct ib_umem *umem, | 
|  | unsigned long pgsz) | 
|  | { | 
|  | return ib_umem_start_dma_addr(umem) & (pgsz - 1); | 
|  | } | 
|  |  | 
|  | static inline size_t ib_umem_num_dma_blocks(struct ib_umem *umem, | 
|  | unsigned long pgsz) | 
|  | { | 
|  | return (size_t)((ALIGN(umem->iova + umem->length, pgsz) - | 
|  | ALIGN_DOWN(umem->iova, pgsz))) / | 
|  | pgsz; | 
|  | } | 
|  |  | 
|  | static inline size_t ib_umem_num_pages(struct ib_umem *umem) | 
|  | { | 
|  | return ib_umem_num_dma_blocks(umem, PAGE_SIZE); | 
|  | } | 
|  |  | 
|  | static inline void __rdma_umem_block_iter_start(struct ib_block_iter *biter, | 
|  | struct ib_umem *umem, | 
|  | unsigned long pgsz) | 
|  | { | 
|  | __rdma_block_iter_start(biter, umem->sgt_append.sgt.sgl, | 
|  | umem->sgt_append.sgt.nents, pgsz); | 
|  | biter->__sg_advance = ib_umem_offset(umem) & ~(pgsz - 1); | 
|  | biter->__sg_numblocks = ib_umem_num_dma_blocks(umem, pgsz); | 
|  | } | 
|  |  | 
|  | static inline bool __rdma_umem_block_iter_next(struct ib_block_iter *biter) | 
|  | { | 
|  | return __rdma_block_iter_next(biter) && biter->__sg_numblocks--; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * rdma_umem_for_each_dma_block - iterate over contiguous DMA blocks of the umem | 
|  | * @umem: umem to iterate over | 
|  | * @pgsz: Page size to split the list into | 
|  | * | 
|  | * pgsz must be <= PAGE_SIZE or computed by ib_umem_find_best_pgsz(). The | 
|  | * returned DMA blocks will be aligned to pgsz and span the range: | 
|  | * ALIGN_DOWN(umem->address, pgsz) to ALIGN(umem->address + umem->length, pgsz) | 
|  | * | 
|  | * Performs exactly ib_umem_num_dma_blocks() iterations. | 
|  | */ | 
|  | #define rdma_umem_for_each_dma_block(umem, biter, pgsz)                        \ | 
|  | for (__rdma_umem_block_iter_start(biter, umem, pgsz);                  \ | 
|  | __rdma_umem_block_iter_next(biter);) | 
|  |  | 
|  | #ifdef CONFIG_INFINIBAND_USER_MEM | 
|  |  | 
|  | struct ib_umem *ib_umem_get(struct ib_device *device, unsigned long addr, | 
|  | size_t size, int access); | 
|  | void ib_umem_release(struct ib_umem *umem); | 
|  | int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, | 
|  | size_t length); | 
|  | unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem, | 
|  | unsigned long pgsz_bitmap, | 
|  | unsigned long virt); | 
|  |  | 
|  | /** | 
|  | * ib_umem_find_best_pgoff - Find best HW page size | 
|  | * | 
|  | * @umem: umem struct | 
|  | * @pgsz_bitmap bitmap of HW supported page sizes | 
|  | * @pgoff_bitmask: Mask of bits that can be represented with an offset | 
|  | * | 
|  | * This is very similar to ib_umem_find_best_pgsz() except instead of accepting | 
|  | * an IOVA it accepts a bitmask specifying what address bits can be represented | 
|  | * with a page offset. | 
|  | * | 
|  | * For instance if the HW has multiple page sizes, requires 64 byte alignemnt, | 
|  | * and can support aligned offsets up to 4032 then pgoff_bitmask would be | 
|  | * "111111000000". | 
|  | * | 
|  | * If the pgoff_bitmask requires either alignment in the low bit or an | 
|  | * unavailable page size for the high bits, this function returns 0. | 
|  | */ | 
|  | static inline unsigned long ib_umem_find_best_pgoff(struct ib_umem *umem, | 
|  | unsigned long pgsz_bitmap, | 
|  | u64 pgoff_bitmask) | 
|  | { | 
|  | dma_addr_t dma_addr; | 
|  |  | 
|  | dma_addr = ib_umem_start_dma_addr(umem); | 
|  | return ib_umem_find_best_pgsz(umem, pgsz_bitmap, | 
|  | dma_addr & pgoff_bitmask); | 
|  | } | 
|  |  | 
|  | static inline bool ib_umem_is_contiguous(struct ib_umem *umem) | 
|  | { | 
|  | dma_addr_t dma_addr; | 
|  | unsigned long pgsz; | 
|  |  | 
|  | /* | 
|  | * Select the smallest aligned page that can contain the whole umem if | 
|  | * it was contiguous. | 
|  | */ | 
|  | dma_addr = ib_umem_start_dma_addr(umem); | 
|  | pgsz = roundup_pow_of_two((dma_addr ^ (umem->length - 1 + dma_addr)) + 1); | 
|  | return !!ib_umem_find_best_pgoff(umem, pgsz, U64_MAX); | 
|  | } | 
|  |  | 
|  | struct ib_umem_dmabuf *ib_umem_dmabuf_get(struct ib_device *device, | 
|  | unsigned long offset, size_t size, | 
|  | int fd, int access, | 
|  | const struct dma_buf_attach_ops *ops); | 
|  | struct ib_umem_dmabuf *ib_umem_dmabuf_get_pinned(struct ib_device *device, | 
|  | unsigned long offset, | 
|  | size_t size, int fd, | 
|  | int access); | 
|  | struct ib_umem_dmabuf * | 
|  | ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, | 
|  | struct device *dma_device, | 
|  | unsigned long offset, size_t size, | 
|  | int fd, int access); | 
|  | int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf); | 
|  | void ib_umem_dmabuf_unmap_pages(struct ib_umem_dmabuf *umem_dmabuf); | 
|  | void ib_umem_dmabuf_release(struct ib_umem_dmabuf *umem_dmabuf); | 
|  | void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf); | 
|  |  | 
|  | #else /* CONFIG_INFINIBAND_USER_MEM */ | 
|  |  | 
|  | #include <linux/err.h> | 
|  |  | 
|  | static inline struct ib_umem *ib_umem_get(struct ib_device *device, | 
|  | unsigned long addr, size_t size, | 
|  | int access) | 
|  | { | 
|  | return ERR_PTR(-EOPNOTSUPP); | 
|  | } | 
|  | static inline void ib_umem_release(struct ib_umem *umem) { } | 
|  | static inline int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, | 
|  | size_t length) { | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  | static inline unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem, | 
|  | unsigned long pgsz_bitmap, | 
|  | unsigned long virt) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | static inline unsigned long ib_umem_find_best_pgoff(struct ib_umem *umem, | 
|  | unsigned long pgsz_bitmap, | 
|  | u64 pgoff_bitmask) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | static inline | 
|  | struct ib_umem_dmabuf *ib_umem_dmabuf_get(struct ib_device *device, | 
|  | unsigned long offset, | 
|  | size_t size, int fd, | 
|  | int access, | 
|  | struct dma_buf_attach_ops *ops) | 
|  | { | 
|  | return ERR_PTR(-EOPNOTSUPP); | 
|  | } | 
|  | static inline struct ib_umem_dmabuf * | 
|  | ib_umem_dmabuf_get_pinned(struct ib_device *device, unsigned long offset, | 
|  | size_t size, int fd, int access) | 
|  | { | 
|  | return ERR_PTR(-EOPNOTSUPP); | 
|  | } | 
|  |  | 
|  | static inline struct ib_umem_dmabuf * | 
|  | ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, | 
|  | struct device *dma_device, | 
|  | unsigned long offset, size_t size, | 
|  | int fd, int access) | 
|  | { | 
|  | return ERR_PTR(-EOPNOTSUPP); | 
|  | } | 
|  |  | 
|  | static inline int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf) | 
|  | { | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  | static inline void ib_umem_dmabuf_unmap_pages(struct ib_umem_dmabuf *umem_dmabuf) { } | 
|  | static inline void ib_umem_dmabuf_release(struct ib_umem_dmabuf *umem_dmabuf) { } | 
|  | static inline void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf) {} | 
|  |  | 
|  | #endif /* CONFIG_INFINIBAND_USER_MEM */ | 
|  | #endif /* IB_UMEM_H */ |