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