Path: blob/master/drivers/infiniband/hw/qib/qib_mmap.c
15112 views
/*1* Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved.2*3* This software is available to you under a choice of one of two4* licenses. You may choose to be licensed under the terms of the GNU5* General Public License (GPL) Version 2, available from the file6* COPYING in the main directory of this source tree, or the7* OpenIB.org BSD license below:8*9* Redistribution and use in source and binary forms, with or10* without modification, are permitted provided that the following11* conditions are met:12*13* - Redistributions of source code must retain the above14* copyright notice, this list of conditions and the following15* disclaimer.16*17* - Redistributions in binary form must reproduce the above18* copyright notice, this list of conditions and the following19* disclaimer in the documentation and/or other materials20* provided with the distribution.21*22* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,23* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF24* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND25* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS26* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN27* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN28* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE29* SOFTWARE.30*/3132#include <linux/module.h>33#include <linux/slab.h>34#include <linux/vmalloc.h>35#include <linux/mm.h>36#include <linux/errno.h>37#include <asm/pgtable.h>3839#include "qib_verbs.h"4041/**42* qib_release_mmap_info - free mmap info structure43* @ref: a pointer to the kref within struct qib_mmap_info44*/45void qib_release_mmap_info(struct kref *ref)46{47struct qib_mmap_info *ip =48container_of(ref, struct qib_mmap_info, ref);49struct qib_ibdev *dev = to_idev(ip->context->device);5051spin_lock_irq(&dev->pending_lock);52list_del(&ip->pending_mmaps);53spin_unlock_irq(&dev->pending_lock);5455vfree(ip->obj);56kfree(ip);57}5859/*60* open and close keep track of how many times the CQ is mapped,61* to avoid releasing it.62*/63static void qib_vma_open(struct vm_area_struct *vma)64{65struct qib_mmap_info *ip = vma->vm_private_data;6667kref_get(&ip->ref);68}6970static void qib_vma_close(struct vm_area_struct *vma)71{72struct qib_mmap_info *ip = vma->vm_private_data;7374kref_put(&ip->ref, qib_release_mmap_info);75}7677static struct vm_operations_struct qib_vm_ops = {78.open = qib_vma_open,79.close = qib_vma_close,80};8182/**83* qib_mmap - create a new mmap region84* @context: the IB user context of the process making the mmap() call85* @vma: the VMA to be initialized86* Return zero if the mmap is OK. Otherwise, return an errno.87*/88int qib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)89{90struct qib_ibdev *dev = to_idev(context->device);91unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;92unsigned long size = vma->vm_end - vma->vm_start;93struct qib_mmap_info *ip, *pp;94int ret = -EINVAL;9596/*97* Search the device's list of objects waiting for a mmap call.98* Normally, this list is very short since a call to create a99* CQ, QP, or SRQ is soon followed by a call to mmap().100*/101spin_lock_irq(&dev->pending_lock);102list_for_each_entry_safe(ip, pp, &dev->pending_mmaps,103pending_mmaps) {104/* Only the creator is allowed to mmap the object */105if (context != ip->context || (__u64) offset != ip->offset)106continue;107/* Don't allow a mmap larger than the object. */108if (size > ip->size)109break;110111list_del_init(&ip->pending_mmaps);112spin_unlock_irq(&dev->pending_lock);113114ret = remap_vmalloc_range(vma, ip->obj, 0);115if (ret)116goto done;117vma->vm_ops = &qib_vm_ops;118vma->vm_private_data = ip;119qib_vma_open(vma);120goto done;121}122spin_unlock_irq(&dev->pending_lock);123done:124return ret;125}126127/*128* Allocate information for qib_mmap129*/130struct qib_mmap_info *qib_create_mmap_info(struct qib_ibdev *dev,131u32 size,132struct ib_ucontext *context,133void *obj) {134struct qib_mmap_info *ip;135136ip = kmalloc(sizeof *ip, GFP_KERNEL);137if (!ip)138goto bail;139140size = PAGE_ALIGN(size);141142spin_lock_irq(&dev->mmap_offset_lock);143if (dev->mmap_offset == 0)144dev->mmap_offset = PAGE_SIZE;145ip->offset = dev->mmap_offset;146dev->mmap_offset += size;147spin_unlock_irq(&dev->mmap_offset_lock);148149INIT_LIST_HEAD(&ip->pending_mmaps);150ip->size = size;151ip->context = context;152ip->obj = obj;153kref_init(&ip->ref);154155bail:156return ip;157}158159void qib_update_mmap_info(struct qib_ibdev *dev, struct qib_mmap_info *ip,160u32 size, void *obj)161{162size = PAGE_ALIGN(size);163164spin_lock_irq(&dev->mmap_offset_lock);165if (dev->mmap_offset == 0)166dev->mmap_offset = PAGE_SIZE;167ip->offset = dev->mmap_offset;168dev->mmap_offset += size;169spin_unlock_irq(&dev->mmap_offset_lock);170171ip->size = size;172ip->obj = obj;173}174175176