1f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell/* 2f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved. 3f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * 4f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * This software is available to you under a choice of one of two 5f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * licenses. You may choose to be licensed under the terms of the GNU 6f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * General Public License (GPL) Version 2, available from the file 7f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * COPYING in the main directory of this source tree, or the 8f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * OpenIB.org BSD license below: 9f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * 10f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * Redistribution and use in source and binary forms, with or 11f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * without modification, are permitted provided that the following 12f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * conditions are met: 13f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * 14f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * - Redistributions of source code must retain the above 15f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * copyright notice, this list of conditions and the following 16f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * disclaimer. 17f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * 18f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * - Redistributions in binary form must reproduce the above 19f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * copyright notice, this list of conditions and the following 20f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * disclaimer in the documentation and/or other materials 21f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * provided with the distribution. 22f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * 23f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * SOFTWARE. 31f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell */ 32f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 33f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include <linux/module.h> 34f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include <linux/slab.h> 35f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include <linux/vmalloc.h> 36f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include <linux/mm.h> 37f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include <linux/errno.h> 38f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include <asm/pgtable.h> 39f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 40f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell#include "qib_verbs.h" 41f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 42f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell/** 43f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * qib_release_mmap_info - free mmap info structure 44f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * @ref: a pointer to the kref within struct qib_mmap_info 45f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell */ 46f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellvoid qib_release_mmap_info(struct kref *ref) 47f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell{ 48f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_mmap_info *ip = 49f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell container_of(ref, struct qib_mmap_info, ref); 50f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_ibdev *dev = to_idev(ip->context->device); 51f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 52f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_lock_irq(&dev->pending_lock); 53f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell list_del(&ip->pending_mmaps); 54f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_unlock_irq(&dev->pending_lock); 55f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 56f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell vfree(ip->obj); 57f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell kfree(ip); 58f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell} 59f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 60f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell/* 61f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * open and close keep track of how many times the CQ is mapped, 62f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * to avoid releasing it. 63f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell */ 64f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellstatic void qib_vma_open(struct vm_area_struct *vma) 65f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell{ 66f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_mmap_info *ip = vma->vm_private_data; 67f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 68f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell kref_get(&ip->ref); 69f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell} 70f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 71f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellstatic void qib_vma_close(struct vm_area_struct *vma) 72f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell{ 73f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_mmap_info *ip = vma->vm_private_data; 74f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 75f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell kref_put(&ip->ref, qib_release_mmap_info); 76f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell} 77f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 78f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellstatic struct vm_operations_struct qib_vm_ops = { 79f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell .open = qib_vma_open, 80f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell .close = qib_vma_close, 81f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell}; 82f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 83f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell/** 84f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * qib_mmap - create a new mmap region 85f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * @context: the IB user context of the process making the mmap() call 86f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * @vma: the VMA to be initialized 87f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * Return zero if the mmap is OK. Otherwise, return an errno. 88f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell */ 89f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellint qib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) 90f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell{ 91f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_ibdev *dev = to_idev(context->device); 92f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 93f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell unsigned long size = vma->vm_end - vma->vm_start; 94f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_mmap_info *ip, *pp; 95f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell int ret = -EINVAL; 96f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 97f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell /* 98f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * Search the device's list of objects waiting for a mmap call. 99f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * Normally, this list is very short since a call to create a 100f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * CQ, QP, or SRQ is soon followed by a call to mmap(). 101f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell */ 102f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_lock_irq(&dev->pending_lock); 103f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell list_for_each_entry_safe(ip, pp, &dev->pending_mmaps, 104f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell pending_mmaps) { 105f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell /* Only the creator is allowed to mmap the object */ 106f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell if (context != ip->context || (__u64) offset != ip->offset) 107f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell continue; 108f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell /* Don't allow a mmap larger than the object. */ 109f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell if (size > ip->size) 110f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell break; 111f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 112f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell list_del_init(&ip->pending_mmaps); 113f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_unlock_irq(&dev->pending_lock); 114f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 115f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ret = remap_vmalloc_range(vma, ip->obj, 0); 116f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell if (ret) 117f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell goto done; 118f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell vma->vm_ops = &qib_vm_ops; 119f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell vma->vm_private_data = ip; 120f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell qib_vma_open(vma); 121f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell goto done; 122f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell } 123f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_unlock_irq(&dev->pending_lock); 124f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbelldone: 125f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell return ret; 126f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell} 127f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 128f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell/* 129f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell * Allocate information for qib_mmap 130f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell */ 131f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellstruct qib_mmap_info *qib_create_mmap_info(struct qib_ibdev *dev, 132f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell u32 size, 133f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct ib_ucontext *context, 134f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell void *obj) { 135f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell struct qib_mmap_info *ip; 136f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 137f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip = kmalloc(sizeof *ip, GFP_KERNEL); 138f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell if (!ip) 139f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell goto bail; 140f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 141f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell size = PAGE_ALIGN(size); 142f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 143f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_lock_irq(&dev->mmap_offset_lock); 144f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell if (dev->mmap_offset == 0) 145f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell dev->mmap_offset = PAGE_SIZE; 146f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->offset = dev->mmap_offset; 147f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell dev->mmap_offset += size; 148f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_unlock_irq(&dev->mmap_offset_lock); 149f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 150f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell INIT_LIST_HEAD(&ip->pending_mmaps); 151f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->size = size; 152f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->context = context; 153f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->obj = obj; 154f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell kref_init(&ip->ref); 155f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 156f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellbail: 157f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell return ip; 158f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell} 159f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 160f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbellvoid qib_update_mmap_info(struct qib_ibdev *dev, struct qib_mmap_info *ip, 161f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell u32 size, void *obj) 162f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell{ 163f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell size = PAGE_ALIGN(size); 164f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 165f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_lock_irq(&dev->mmap_offset_lock); 166f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell if (dev->mmap_offset == 0) 167f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell dev->mmap_offset = PAGE_SIZE; 168f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->offset = dev->mmap_offset; 169f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell dev->mmap_offset += size; 170f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell spin_unlock_irq(&dev->mmap_offset_lock); 171f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell 172f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->size = size; 173f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell ip->obj = obj; 174f931551bafe1f10ded7f5282e2aa162c267a2e5dRalph Campbell} 175