18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2016 Intel Corporation. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 168c2ecf20Sopenharmony_ci * General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * BSD LICENSE 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 228c2ecf20Sopenharmony_ci * are met: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 258c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 268c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 278c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 288c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 298c2ecf20Sopenharmony_ci * distribution. 308c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 318c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 328c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/slab.h> 498c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 508c2ecf20Sopenharmony_ci#include <linux/mm.h> 518c2ecf20Sopenharmony_ci#include <rdma/uverbs_ioctl.h> 528c2ecf20Sopenharmony_ci#include "mmap.h" 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * rvt_mmap_init - init link list and lock for mem map 568c2ecf20Sopenharmony_ci * @rdi: rvt dev struct 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_civoid rvt_mmap_init(struct rvt_dev_info *rdi) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdi->pending_mmaps); 618c2ecf20Sopenharmony_ci spin_lock_init(&rdi->pending_lock); 628c2ecf20Sopenharmony_ci rdi->mmap_offset = PAGE_SIZE; 638c2ecf20Sopenharmony_ci spin_lock_init(&rdi->mmap_offset_lock); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/** 678c2ecf20Sopenharmony_ci * rvt_release_mmap_info - free mmap info structure 688c2ecf20Sopenharmony_ci * @ref: a pointer to the kref within struct rvt_mmap_info 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_civoid rvt_release_mmap_info(struct kref *ref) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct rvt_mmap_info *ip = 738c2ecf20Sopenharmony_ci container_of(ref, struct rvt_mmap_info, ref); 748c2ecf20Sopenharmony_ci struct rvt_dev_info *rdi = ib_to_rvt(ip->context->device); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock_irq(&rdi->pending_lock); 778c2ecf20Sopenharmony_ci list_del(&ip->pending_mmaps); 788c2ecf20Sopenharmony_ci spin_unlock_irq(&rdi->pending_lock); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci vfree(ip->obj); 818c2ecf20Sopenharmony_ci kfree(ip); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void rvt_vma_open(struct vm_area_struct *vma) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct rvt_mmap_info *ip = vma->vm_private_data; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci kref_get(&ip->ref); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void rvt_vma_close(struct vm_area_struct *vma) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct rvt_mmap_info *ip = vma->vm_private_data; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci kref_put(&ip->ref, rvt_release_mmap_info); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct vm_operations_struct rvt_vm_ops = { 998c2ecf20Sopenharmony_ci .open = rvt_vma_open, 1008c2ecf20Sopenharmony_ci .close = rvt_vma_close, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * rvt_mmap - create a new mmap region 1058c2ecf20Sopenharmony_ci * @context: the IB user context of the process making the mmap() call 1068c2ecf20Sopenharmony_ci * @vma: the VMA to be initialized 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * Return: zero if the mmap is OK. Otherwise, return an errno. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ciint rvt_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct rvt_dev_info *rdi = ib_to_rvt(context->device); 1138c2ecf20Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 1148c2ecf20Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 1158c2ecf20Sopenharmony_ci struct rvt_mmap_info *ip, *pp; 1168c2ecf20Sopenharmony_ci int ret = -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* 1198c2ecf20Sopenharmony_ci * Search the device's list of objects waiting for a mmap call. 1208c2ecf20Sopenharmony_ci * Normally, this list is very short since a call to create a 1218c2ecf20Sopenharmony_ci * CQ, QP, or SRQ is soon followed by a call to mmap(). 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci spin_lock_irq(&rdi->pending_lock); 1248c2ecf20Sopenharmony_ci list_for_each_entry_safe(ip, pp, &rdi->pending_mmaps, 1258c2ecf20Sopenharmony_ci pending_mmaps) { 1268c2ecf20Sopenharmony_ci /* Only the creator is allowed to mmap the object */ 1278c2ecf20Sopenharmony_ci if (context != ip->context || (__u64)offset != ip->offset) 1288c2ecf20Sopenharmony_ci continue; 1298c2ecf20Sopenharmony_ci /* Don't allow a mmap larger than the object. */ 1308c2ecf20Sopenharmony_ci if (size > ip->size) 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci list_del_init(&ip->pending_mmaps); 1348c2ecf20Sopenharmony_ci spin_unlock_irq(&rdi->pending_lock); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = remap_vmalloc_range(vma, ip->obj, 0); 1378c2ecf20Sopenharmony_ci if (ret) 1388c2ecf20Sopenharmony_ci goto done; 1398c2ecf20Sopenharmony_ci vma->vm_ops = &rvt_vm_ops; 1408c2ecf20Sopenharmony_ci vma->vm_private_data = ip; 1418c2ecf20Sopenharmony_ci rvt_vma_open(vma); 1428c2ecf20Sopenharmony_ci goto done; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci spin_unlock_irq(&rdi->pending_lock); 1458c2ecf20Sopenharmony_cidone: 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/** 1508c2ecf20Sopenharmony_ci * rvt_create_mmap_info - allocate information for hfi1_mmap 1518c2ecf20Sopenharmony_ci * @rdi: rvt dev struct 1528c2ecf20Sopenharmony_ci * @size: size in bytes to map 1538c2ecf20Sopenharmony_ci * @udata: user data (must be valid!) 1548c2ecf20Sopenharmony_ci * @obj: opaque pointer to a cq, wq etc 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Return: rvt_mmap struct on success, ERR_PTR on failure 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistruct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size, 1598c2ecf20Sopenharmony_ci struct ib_udata *udata, void *obj) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct rvt_mmap_info *ip; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!udata) 1648c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ip = kmalloc_node(sizeof(*ip), GFP_KERNEL, rdi->dparms.node); 1678c2ecf20Sopenharmony_ci if (!ip) 1688c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci size = PAGE_ALIGN(size); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_lock_irq(&rdi->mmap_offset_lock); 1738c2ecf20Sopenharmony_ci if (rdi->mmap_offset == 0) 1748c2ecf20Sopenharmony_ci rdi->mmap_offset = ALIGN(PAGE_SIZE, SHMLBA); 1758c2ecf20Sopenharmony_ci ip->offset = rdi->mmap_offset; 1768c2ecf20Sopenharmony_ci rdi->mmap_offset += ALIGN(size, SHMLBA); 1778c2ecf20Sopenharmony_ci spin_unlock_irq(&rdi->mmap_offset_lock); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ip->pending_mmaps); 1808c2ecf20Sopenharmony_ci ip->size = size; 1818c2ecf20Sopenharmony_ci ip->context = 1828c2ecf20Sopenharmony_ci container_of(udata, struct uverbs_attr_bundle, driver_udata) 1838c2ecf20Sopenharmony_ci ->context; 1848c2ecf20Sopenharmony_ci ip->obj = obj; 1858c2ecf20Sopenharmony_ci kref_init(&ip->ref); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return ip; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * rvt_update_mmap_info - update a mem map 1928c2ecf20Sopenharmony_ci * @rdi: rvt dev struct 1938c2ecf20Sopenharmony_ci * @ip: mmap info pointer 1948c2ecf20Sopenharmony_ci * @size: size to grow by 1958c2ecf20Sopenharmony_ci * @obj: opaque pointer to cq, wq, etc. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_civoid rvt_update_mmap_info(struct rvt_dev_info *rdi, struct rvt_mmap_info *ip, 1988c2ecf20Sopenharmony_ci u32 size, void *obj) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci size = PAGE_ALIGN(size); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock_irq(&rdi->mmap_offset_lock); 2038c2ecf20Sopenharmony_ci if (rdi->mmap_offset == 0) 2048c2ecf20Sopenharmony_ci rdi->mmap_offset = PAGE_SIZE; 2058c2ecf20Sopenharmony_ci ip->offset = rdi->mmap_offset; 2068c2ecf20Sopenharmony_ci rdi->mmap_offset += size; 2078c2ecf20Sopenharmony_ci spin_unlock_irq(&rdi->mmap_offset_lock); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ip->size = size; 2108c2ecf20Sopenharmony_ci ip->obj = obj; 2118c2ecf20Sopenharmony_ci} 212