xref: /linux/drivers/infiniband/sw/rdmavt/mmap.c (revision fc2d791a43d3880496d1c729b8bd74d2c19cb4e7)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright(c) 2016 Intel Corporation.
4  */
5 
6 #include <linux/slab.h>
7 #include <linux/vmalloc.h>
8 #include <linux/mm.h>
9 #include <rdma/uverbs_ioctl.h>
10 #include "mmap.h"
11 
12 /* number of reserved mmaps for the driver */
13 #define MMAP_RESERVED 256
14 /* start point for dynamic offsets */
15 #define MMAP_OFFSET_START (MMAP_RESERVED * PAGE_SIZE)
16 
17 /**
18  * rvt_mmap_init - init link list and lock for mem map
19  * @rdi: rvt dev struct
20  */
21 void rvt_mmap_init(struct rvt_dev_info *rdi)
22 {
23 	INIT_LIST_HEAD(&rdi->pending_mmaps);
24 	spin_lock_init(&rdi->pending_lock);
25 	rdi->mmap_offset = MMAP_OFFSET_START;
26 	spin_lock_init(&rdi->mmap_offset_lock);
27 }
28 
29 /**
30  * rvt_release_mmap_info - free mmap info structure
31  * @ref: a pointer to the kref within struct rvt_mmap_info
32  */
33 void rvt_release_mmap_info(struct kref *ref)
34 {
35 	struct rvt_mmap_info *ip =
36 		container_of(ref, struct rvt_mmap_info, ref);
37 	struct rvt_dev_info *rdi = ib_to_rvt(ip->context->device);
38 
39 	spin_lock_irq(&rdi->pending_lock);
40 	list_del(&ip->pending_mmaps);
41 	spin_unlock_irq(&rdi->pending_lock);
42 
43 	vfree(ip->obj);
44 	kfree(ip);
45 }
46 
47 static void rvt_vma_open(struct vm_area_struct *vma)
48 {
49 	struct rvt_mmap_info *ip = vma->vm_private_data;
50 
51 	kref_get(&ip->ref);
52 }
53 
54 static void rvt_vma_close(struct vm_area_struct *vma)
55 {
56 	struct rvt_mmap_info *ip = vma->vm_private_data;
57 
58 	kref_put(&ip->ref, rvt_release_mmap_info);
59 }
60 
61 static const struct vm_operations_struct rvt_vm_ops = {
62 	.open = rvt_vma_open,
63 	.close = rvt_vma_close,
64 };
65 
66 /**
67  * rvt_mmap - create a new mmap region
68  * @context: the IB user context of the process making the mmap() call
69  * @vma: the VMA to be initialized
70  *
71  * Return: zero if the mmap is OK. Otherwise, return an errno.
72  */
73 int rvt_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
74 {
75 	struct rvt_dev_info *rdi = ib_to_rvt(context->device);
76 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
77 	unsigned long size = vma->vm_end - vma->vm_start;
78 	struct rvt_mmap_info *ip, *pp;
79 	int ret = -EINVAL;
80 
81 	/* call driver if in reserved range */
82 	if (offset < MMAP_OFFSET_START) {
83 		if (rdi->driver_f.mmap)
84 			return rdi->driver_f.mmap(context, vma);
85 		return -EINVAL;
86 	}
87 
88 	/*
89 	 * Search the device's list of objects waiting for a mmap call.
90 	 * Normally, this list is very short since a call to create a
91 	 * CQ, QP, or SRQ is soon followed by a call to mmap().
92 	 */
93 	spin_lock_irq(&rdi->pending_lock);
94 	list_for_each_entry_safe(ip, pp, &rdi->pending_mmaps,
95 				 pending_mmaps) {
96 		/* Only the creator is allowed to mmap the object */
97 		if (context != ip->context || (__u64)offset != ip->offset)
98 			continue;
99 		/* Don't allow a mmap larger than the object. */
100 		if (size > ip->size)
101 			break;
102 
103 		list_del_init(&ip->pending_mmaps);
104 		spin_unlock_irq(&rdi->pending_lock);
105 
106 		ret = remap_vmalloc_range(vma, ip->obj, 0);
107 		if (ret)
108 			goto done;
109 		vma->vm_ops = &rvt_vm_ops;
110 		vma->vm_private_data = ip;
111 		rvt_vma_open(vma);
112 		goto done;
113 	}
114 	spin_unlock_irq(&rdi->pending_lock);
115 done:
116 	return ret;
117 }
118 
119 /**
120  * rvt_create_mmap_info - allocate information for hfi1_mmap
121  * @rdi: rvt dev struct
122  * @size: size in bytes to map
123  * @udata: user data (must be valid!)
124  * @obj: opaque pointer to a cq, wq etc
125  *
126  * Return: rvt_mmap struct on success, ERR_PTR on failure
127  */
128 struct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size,
129 					   struct ib_udata *udata, void *obj)
130 {
131 	struct rvt_mmap_info *ip;
132 
133 	if (!udata)
134 		return ERR_PTR(-EINVAL);
135 
136 	ip = kmalloc_node(sizeof(*ip), GFP_KERNEL, rdi->dparms.node);
137 	if (!ip)
138 		return ERR_PTR(-ENOMEM);
139 
140 	size = PAGE_ALIGN(size);
141 
142 	spin_lock_irq(&rdi->mmap_offset_lock);
143 	if (rdi->mmap_offset == 0)
144 		rdi->mmap_offset = MMAP_OFFSET_START;
145 	ip->offset = rdi->mmap_offset;
146 	rdi->mmap_offset += PAGE_SIZE;
147 	spin_unlock_irq(&rdi->mmap_offset_lock);
148 
149 	INIT_LIST_HEAD(&ip->pending_mmaps);
150 	ip->size = size;
151 	ip->context =
152 		container_of(udata, struct uverbs_attr_bundle, driver_udata)
153 			->context;
154 	ip->obj = obj;
155 	kref_init(&ip->ref);
156 
157 	return ip;
158 }
159 
160 /**
161  * rvt_update_mmap_info - update a mem map
162  * @rdi: rvt dev struct
163  * @ip: mmap info pointer
164  * @size: size to grow by
165  * @obj: opaque pointer to cq, wq, etc.
166  */
167 void rvt_update_mmap_info(struct rvt_dev_info *rdi, struct rvt_mmap_info *ip,
168 			  u32 size, void *obj)
169 {
170 	size = PAGE_ALIGN(size);
171 
172 	spin_lock_irq(&rdi->mmap_offset_lock);
173 	if (rdi->mmap_offset == 0)
174 		rdi->mmap_offset = MMAP_OFFSET_START;
175 	ip->offset = rdi->mmap_offset;
176 	rdi->mmap_offset += PAGE_SIZE;
177 	spin_unlock_irq(&rdi->mmap_offset_lock);
178 
179 	ip->size = size;
180 	ip->obj = obj;
181 }
182