162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2016 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/vmalloc.h> 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <rdma/uverbs_ioctl.h> 1062306a36Sopenharmony_ci#include "mmap.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * rvt_mmap_init - init link list and lock for mem map 1462306a36Sopenharmony_ci * @rdi: rvt dev struct 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_civoid rvt_mmap_init(struct rvt_dev_info *rdi) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci INIT_LIST_HEAD(&rdi->pending_mmaps); 1962306a36Sopenharmony_ci spin_lock_init(&rdi->pending_lock); 2062306a36Sopenharmony_ci rdi->mmap_offset = PAGE_SIZE; 2162306a36Sopenharmony_ci spin_lock_init(&rdi->mmap_offset_lock); 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * rvt_release_mmap_info - free mmap info structure 2662306a36Sopenharmony_ci * @ref: a pointer to the kref within struct rvt_mmap_info 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_civoid rvt_release_mmap_info(struct kref *ref) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct rvt_mmap_info *ip = 3162306a36Sopenharmony_ci container_of(ref, struct rvt_mmap_info, ref); 3262306a36Sopenharmony_ci struct rvt_dev_info *rdi = ib_to_rvt(ip->context->device); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci spin_lock_irq(&rdi->pending_lock); 3562306a36Sopenharmony_ci list_del(&ip->pending_mmaps); 3662306a36Sopenharmony_ci spin_unlock_irq(&rdi->pending_lock); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci vfree(ip->obj); 3962306a36Sopenharmony_ci kfree(ip); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void rvt_vma_open(struct vm_area_struct *vma) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct rvt_mmap_info *ip = vma->vm_private_data; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci kref_get(&ip->ref); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void rvt_vma_close(struct vm_area_struct *vma) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct rvt_mmap_info *ip = vma->vm_private_data; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci kref_put(&ip->ref, rvt_release_mmap_info); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const struct vm_operations_struct rvt_vm_ops = { 5762306a36Sopenharmony_ci .open = rvt_vma_open, 5862306a36Sopenharmony_ci .close = rvt_vma_close, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/** 6262306a36Sopenharmony_ci * rvt_mmap - create a new mmap region 6362306a36Sopenharmony_ci * @context: the IB user context of the process making the mmap() call 6462306a36Sopenharmony_ci * @vma: the VMA to be initialized 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * Return: zero if the mmap is OK. Otherwise, return an errno. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ciint rvt_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct rvt_dev_info *rdi = ib_to_rvt(context->device); 7162306a36Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 7262306a36Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 7362306a36Sopenharmony_ci struct rvt_mmap_info *ip, *pp; 7462306a36Sopenharmony_ci int ret = -EINVAL; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Search the device's list of objects waiting for a mmap call. 7862306a36Sopenharmony_ci * Normally, this list is very short since a call to create a 7962306a36Sopenharmony_ci * CQ, QP, or SRQ is soon followed by a call to mmap(). 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci spin_lock_irq(&rdi->pending_lock); 8262306a36Sopenharmony_ci list_for_each_entry_safe(ip, pp, &rdi->pending_mmaps, 8362306a36Sopenharmony_ci pending_mmaps) { 8462306a36Sopenharmony_ci /* Only the creator is allowed to mmap the object */ 8562306a36Sopenharmony_ci if (context != ip->context || (__u64)offset != ip->offset) 8662306a36Sopenharmony_ci continue; 8762306a36Sopenharmony_ci /* Don't allow a mmap larger than the object. */ 8862306a36Sopenharmony_ci if (size > ip->size) 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci list_del_init(&ip->pending_mmaps); 9262306a36Sopenharmony_ci spin_unlock_irq(&rdi->pending_lock); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ret = remap_vmalloc_range(vma, ip->obj, 0); 9562306a36Sopenharmony_ci if (ret) 9662306a36Sopenharmony_ci goto done; 9762306a36Sopenharmony_ci vma->vm_ops = &rvt_vm_ops; 9862306a36Sopenharmony_ci vma->vm_private_data = ip; 9962306a36Sopenharmony_ci rvt_vma_open(vma); 10062306a36Sopenharmony_ci goto done; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci spin_unlock_irq(&rdi->pending_lock); 10362306a36Sopenharmony_cidone: 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/** 10862306a36Sopenharmony_ci * rvt_create_mmap_info - allocate information for hfi1_mmap 10962306a36Sopenharmony_ci * @rdi: rvt dev struct 11062306a36Sopenharmony_ci * @size: size in bytes to map 11162306a36Sopenharmony_ci * @udata: user data (must be valid!) 11262306a36Sopenharmony_ci * @obj: opaque pointer to a cq, wq etc 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Return: rvt_mmap struct on success, ERR_PTR on failure 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistruct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size, 11762306a36Sopenharmony_ci struct ib_udata *udata, void *obj) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct rvt_mmap_info *ip; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!udata) 12262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ip = kmalloc_node(sizeof(*ip), GFP_KERNEL, rdi->dparms.node); 12562306a36Sopenharmony_ci if (!ip) 12662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci size = PAGE_ALIGN(size); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci spin_lock_irq(&rdi->mmap_offset_lock); 13162306a36Sopenharmony_ci if (rdi->mmap_offset == 0) 13262306a36Sopenharmony_ci rdi->mmap_offset = ALIGN(PAGE_SIZE, SHMLBA); 13362306a36Sopenharmony_ci ip->offset = rdi->mmap_offset; 13462306a36Sopenharmony_ci rdi->mmap_offset += ALIGN(size, SHMLBA); 13562306a36Sopenharmony_ci spin_unlock_irq(&rdi->mmap_offset_lock); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci INIT_LIST_HEAD(&ip->pending_mmaps); 13862306a36Sopenharmony_ci ip->size = size; 13962306a36Sopenharmony_ci ip->context = 14062306a36Sopenharmony_ci container_of(udata, struct uverbs_attr_bundle, driver_udata) 14162306a36Sopenharmony_ci ->context; 14262306a36Sopenharmony_ci ip->obj = obj; 14362306a36Sopenharmony_ci kref_init(&ip->ref); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return ip; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * rvt_update_mmap_info - update a mem map 15062306a36Sopenharmony_ci * @rdi: rvt dev struct 15162306a36Sopenharmony_ci * @ip: mmap info pointer 15262306a36Sopenharmony_ci * @size: size to grow by 15362306a36Sopenharmony_ci * @obj: opaque pointer to cq, wq, etc. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_civoid rvt_update_mmap_info(struct rvt_dev_info *rdi, struct rvt_mmap_info *ip, 15662306a36Sopenharmony_ci u32 size, void *obj) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci size = PAGE_ALIGN(size); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci spin_lock_irq(&rdi->mmap_offset_lock); 16162306a36Sopenharmony_ci if (rdi->mmap_offset == 0) 16262306a36Sopenharmony_ci rdi->mmap_offset = PAGE_SIZE; 16362306a36Sopenharmony_ci ip->offset = rdi->mmap_offset; 16462306a36Sopenharmony_ci rdi->mmap_offset += size; 16562306a36Sopenharmony_ci spin_unlock_irq(&rdi->mmap_offset_lock); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ip->size = size; 16862306a36Sopenharmony_ci ip->obj = obj; 16962306a36Sopenharmony_ci} 170