Loading...
Note: File does not exist in v3.1.
1// SPDX-License-Identifier: GPL-2.0
2
3/*
4 * Copyright 2022 HabanaLabs, Ltd.
5 * All Rights Reserved.
6 */
7
8#include "habanalabs.h"
9
10/**
11 * hl_mmap_mem_buf_get - increase the buffer refcount and return a pointer to
12 * the buffer descriptor.
13 *
14 * @mmg: parent unified memory manager
15 * @handle: requested buffer handle
16 *
17 * Find the buffer in the store and return a pointer to its descriptor.
18 * Increase buffer refcount. If not found - return NULL.
19 */
20struct hl_mmap_mem_buf *hl_mmap_mem_buf_get(struct hl_mem_mgr *mmg, u64 handle)
21{
22 struct hl_mmap_mem_buf *buf;
23
24 spin_lock(&mmg->lock);
25 buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
26 if (!buf) {
27 spin_unlock(&mmg->lock);
28 dev_warn(mmg->dev,
29 "Buff get failed, no match to handle %#llx\n", handle);
30 return NULL;
31 }
32 kref_get(&buf->refcount);
33 spin_unlock(&mmg->lock);
34 return buf;
35}
36
37/**
38 * hl_mmap_mem_buf_destroy - destroy the unused buffer
39 *
40 * @buf: memory manager buffer descriptor
41 *
42 * Internal function, used as a final step of buffer release. Shall be invoked
43 * only when the buffer is no longer in use (removed from idr). Will call the
44 * release callback (if applicable), and free the memory.
45 */
46static void hl_mmap_mem_buf_destroy(struct hl_mmap_mem_buf *buf)
47{
48 if (buf->behavior->release)
49 buf->behavior->release(buf);
50
51 kfree(buf);
52}
53
54/**
55 * hl_mmap_mem_buf_release - release buffer
56 *
57 * @kref: kref that reached 0.
58 *
59 * Internal function, used as a kref release callback, when the last user of
60 * the buffer is released. Shall be called from an interrupt context.
61 */
62static void hl_mmap_mem_buf_release(struct kref *kref)
63{
64 struct hl_mmap_mem_buf *buf =
65 container_of(kref, struct hl_mmap_mem_buf, refcount);
66
67 spin_lock(&buf->mmg->lock);
68 idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
69 spin_unlock(&buf->mmg->lock);
70
71 hl_mmap_mem_buf_destroy(buf);
72}
73
74/**
75 * hl_mmap_mem_buf_remove_idr_locked - remove handle from idr
76 *
77 * @kref: kref that reached 0.
78 *
79 * Internal function, used for kref put by handle. Assumes mmg lock is taken.
80 * Will remove the buffer from idr, without destroying it.
81 */
82static void hl_mmap_mem_buf_remove_idr_locked(struct kref *kref)
83{
84 struct hl_mmap_mem_buf *buf =
85 container_of(kref, struct hl_mmap_mem_buf, refcount);
86
87 idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
88}
89
90/**
91 * hl_mmap_mem_buf_put - decrease the reference to the buffer
92 *
93 * @buf: memory manager buffer descriptor
94 *
95 * Decrease the reference to the buffer, and release it if it was the last one.
96 * Shall be called from an interrupt context.
97 */
98int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf)
99{
100 return kref_put(&buf->refcount, hl_mmap_mem_buf_release);
101}
102
103/**
104 * hl_mmap_mem_buf_put_handle - decrease the reference to the buffer with the
105 * given handle.
106 *
107 * @mmg: parent unified memory manager
108 * @handle: requested buffer handle
109 *
110 * Decrease the reference to the buffer, and release it if it was the last one.
111 * Shall not be called from an interrupt context. Return -EINVAL if handle was
112 * not found, else return the put outcome (0 or 1).
113 */
114int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle)
115{
116 struct hl_mmap_mem_buf *buf;
117
118 spin_lock(&mmg->lock);
119 buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
120 if (!buf) {
121 spin_unlock(&mmg->lock);
122 dev_dbg(mmg->dev,
123 "Buff put failed, no match to handle %#llx\n", handle);
124 return -EINVAL;
125 }
126
127 if (kref_put(&buf->refcount, hl_mmap_mem_buf_remove_idr_locked)) {
128 spin_unlock(&mmg->lock);
129 hl_mmap_mem_buf_destroy(buf);
130 return 1;
131 }
132
133 spin_unlock(&mmg->lock);
134 return 0;
135}
136
137/**
138 * hl_mmap_mem_buf_alloc - allocate a new mappable buffer
139 *
140 * @mmg: parent unified memory manager
141 * @behavior: behavior object describing this buffer polymorphic behavior
142 * @gfp: gfp flags to use for the memory allocations
143 * @args: additional args passed to behavior->alloc
144 *
145 * Allocate and register a new memory buffer inside the give memory manager.
146 * Return the pointer to the new buffer on success or NULL on failure.
147 */
148struct hl_mmap_mem_buf *
149hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
150 struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp,
151 void *args)
152{
153 struct hl_mmap_mem_buf *buf;
154 int rc;
155
156 buf = kzalloc(sizeof(*buf), gfp);
157 if (!buf)
158 return NULL;
159
160 spin_lock(&mmg->lock);
161 rc = idr_alloc(&mmg->handles, buf, 1, 0, GFP_ATOMIC);
162 spin_unlock(&mmg->lock);
163 if (rc < 0) {
164 dev_err(mmg->dev,
165 "%s: Failed to allocate IDR for a new buffer, rc=%d\n",
166 behavior->topic, rc);
167 goto free_buf;
168 }
169
170 buf->mmg = mmg;
171 buf->behavior = behavior;
172 buf->handle = (((u64)rc | buf->behavior->mem_id) << PAGE_SHIFT);
173 kref_init(&buf->refcount);
174
175 rc = buf->behavior->alloc(buf, gfp, args);
176 if (rc) {
177 dev_err(mmg->dev, "%s: Failure in buffer alloc callback %d\n",
178 behavior->topic, rc);
179 goto remove_idr;
180 }
181
182 return buf;
183
184remove_idr:
185 spin_lock(&mmg->lock);
186 idr_remove(&mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
187 spin_unlock(&mmg->lock);
188free_buf:
189 kfree(buf);
190 return NULL;
191}
192
193/**
194 * hl_mmap_mem_buf_vm_close - handle mmap close
195 *
196 * @vma: the vma object for which mmap was closed.
197 *
198 * Put the memory buffer if it is no longer mapped.
199 */
200static void hl_mmap_mem_buf_vm_close(struct vm_area_struct *vma)
201{
202 struct hl_mmap_mem_buf *buf =
203 (struct hl_mmap_mem_buf *)vma->vm_private_data;
204 long new_mmap_size;
205
206 new_mmap_size = buf->real_mapped_size - (vma->vm_end - vma->vm_start);
207
208 if (new_mmap_size > 0) {
209 buf->real_mapped_size = new_mmap_size;
210 return;
211 }
212
213 atomic_set(&buf->mmap, 0);
214 hl_mmap_mem_buf_put(buf);
215 vma->vm_private_data = NULL;
216}
217
218static const struct vm_operations_struct hl_mmap_mem_buf_vm_ops = {
219 .close = hl_mmap_mem_buf_vm_close
220};
221
222/**
223 * hl_mem_mgr_mmap - map the given buffer to the user
224 *
225 * @mmg: unified memory manager
226 * @vma: the vma object for which mmap was closed.
227 * @args: additional args passed to behavior->mmap
228 *
229 * Map the buffer specified by the vma->vm_pgoff to the given vma.
230 */
231int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
232 void *args)
233{
234 struct hl_mmap_mem_buf *buf;
235 u64 user_mem_size;
236 u64 handle;
237 int rc;
238
239 /* We use the page offset to hold the idr and thus we need to clear
240 * it before doing the mmap itself
241 */
242 handle = vma->vm_pgoff << PAGE_SHIFT;
243 vma->vm_pgoff = 0;
244
245 /* Reference was taken here */
246 buf = hl_mmap_mem_buf_get(mmg, handle);
247 if (!buf) {
248 dev_err(mmg->dev,
249 "Memory mmap failed, no match to handle %#llx\n", handle);
250 return -EINVAL;
251 }
252
253 /* Validation check */
254 user_mem_size = vma->vm_end - vma->vm_start;
255 if (user_mem_size != ALIGN(buf->mappable_size, PAGE_SIZE)) {
256 dev_err(mmg->dev,
257 "%s: Memory mmap failed, mmap VM size 0x%llx != 0x%llx allocated physical mem size\n",
258 buf->behavior->topic, user_mem_size, buf->mappable_size);
259 rc = -EINVAL;
260 goto put_mem;
261 }
262
263#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK
264 if (!access_ok(VERIFY_WRITE, (void __user *)(uintptr_t)vma->vm_start,
265 user_mem_size)) {
266#else
267 if (!access_ok((void __user *)(uintptr_t)vma->vm_start,
268 user_mem_size)) {
269#endif
270 dev_err(mmg->dev, "%s: User pointer is invalid - 0x%lx\n",
271 buf->behavior->topic, vma->vm_start);
272
273 rc = -EINVAL;
274 goto put_mem;
275 }
276
277 if (atomic_cmpxchg(&buf->mmap, 0, 1)) {
278 dev_err(mmg->dev,
279 "%s, Memory mmap failed, already mmaped to user\n",
280 buf->behavior->topic);
281 rc = -EINVAL;
282 goto put_mem;
283 }
284
285 vma->vm_ops = &hl_mmap_mem_buf_vm_ops;
286
287 /* Note: We're transferring the memory reference to vma->vm_private_data here. */
288
289 vma->vm_private_data = buf;
290
291 rc = buf->behavior->mmap(buf, vma, args);
292 if (rc) {
293 atomic_set(&buf->mmap, 0);
294 goto put_mem;
295 }
296
297 buf->real_mapped_size = buf->mappable_size;
298 vma->vm_pgoff = handle >> PAGE_SHIFT;
299
300 return 0;
301
302put_mem:
303 hl_mmap_mem_buf_put(buf);
304 return rc;
305}
306
307/**
308 * hl_mem_mgr_init - initialize unified memory manager
309 *
310 * @dev: owner device pointer
311 * @mmg: structure to initialize
312 *
313 * Initialize an instance of unified memory manager
314 */
315void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg)
316{
317 mmg->dev = dev;
318 spin_lock_init(&mmg->lock);
319 idr_init(&mmg->handles);
320}
321
322/**
323 * hl_mem_mgr_fini - release unified memory manager
324 *
325 * @mmg: parent unified memory manager
326 *
327 * Release the unified memory manager. Shall be called from an interrupt context.
328 */
329void hl_mem_mgr_fini(struct hl_mem_mgr *mmg)
330{
331 struct hl_mmap_mem_buf *buf;
332 struct idr *idp;
333 const char *topic;
334 u32 id;
335
336 idp = &mmg->handles;
337
338 idr_for_each_entry(idp, buf, id) {
339 topic = buf->behavior->topic;
340 if (hl_mmap_mem_buf_put(buf) != 1)
341 dev_err(mmg->dev,
342 "%s: Buff handle %u for CTX is still alive\n",
343 topic, id);
344 }
345
346 /* TODO: can it happen that some buffer is still in use at this point? */
347
348 idr_destroy(&mmg->handles);
349}