Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/accel/habanalabs/common/memory_mgr.c
26436 views
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
*/
20
struct 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_dbg(mmg->dev, "Buff get failed, no match to handle %#llx\n", handle);
29
return NULL;
30
}
31
kref_get(&buf->refcount);
32
spin_unlock(&mmg->lock);
33
return buf;
34
}
35
36
/**
37
* hl_mmap_mem_buf_destroy - destroy the unused buffer
38
*
39
* @buf: memory manager buffer descriptor
40
*
41
* Internal function, used as a final step of buffer release. Shall be invoked
42
* only when the buffer is no longer in use (removed from idr). Will call the
43
* release callback (if applicable), and free the memory.
44
*/
45
static void hl_mmap_mem_buf_destroy(struct hl_mmap_mem_buf *buf)
46
{
47
if (buf->behavior->release)
48
buf->behavior->release(buf);
49
50
kfree(buf);
51
}
52
53
/**
54
* hl_mmap_mem_buf_release - release buffer
55
*
56
* @kref: kref that reached 0.
57
*
58
* Internal function, used as a kref release callback, when the last user of
59
* the buffer is released. Shall be called from an interrupt context.
60
*/
61
static void hl_mmap_mem_buf_release(struct kref *kref)
62
{
63
struct hl_mmap_mem_buf *buf =
64
container_of(kref, struct hl_mmap_mem_buf, refcount);
65
66
spin_lock(&buf->mmg->lock);
67
idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
68
spin_unlock(&buf->mmg->lock);
69
70
hl_mmap_mem_buf_destroy(buf);
71
}
72
73
/**
74
* hl_mmap_mem_buf_remove_idr_locked - remove handle from idr
75
*
76
* @kref: kref that reached 0.
77
*
78
* Internal function, used for kref put by handle. Assumes mmg lock is taken.
79
* Will remove the buffer from idr, without destroying it.
80
*/
81
static void hl_mmap_mem_buf_remove_idr_locked(struct kref *kref)
82
{
83
struct hl_mmap_mem_buf *buf =
84
container_of(kref, struct hl_mmap_mem_buf, refcount);
85
86
idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
87
}
88
89
/**
90
* hl_mmap_mem_buf_put - decrease the reference to the buffer
91
*
92
* @buf: memory manager buffer descriptor
93
*
94
* Decrease the reference to the buffer, and release it if it was the last one.
95
* Shall be called from an interrupt context.
96
*/
97
int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf)
98
{
99
return kref_put(&buf->refcount, hl_mmap_mem_buf_release);
100
}
101
102
/**
103
* hl_mmap_mem_buf_put_handle - decrease the reference to the buffer with the
104
* given handle.
105
*
106
* @mmg: parent unified memory manager
107
* @handle: requested buffer handle
108
*
109
* Decrease the reference to the buffer, and release it if it was the last one.
110
* Shall not be called from an interrupt context. Return -EINVAL if handle was
111
* not found, else return the put outcome (0 or 1).
112
*/
113
int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle)
114
{
115
struct hl_mmap_mem_buf *buf;
116
117
spin_lock(&mmg->lock);
118
buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
119
if (!buf) {
120
spin_unlock(&mmg->lock);
121
dev_dbg(mmg->dev,
122
"Buff put failed, no match to handle %#llx\n", handle);
123
return -EINVAL;
124
}
125
126
if (kref_put(&buf->refcount, hl_mmap_mem_buf_remove_idr_locked)) {
127
spin_unlock(&mmg->lock);
128
hl_mmap_mem_buf_destroy(buf);
129
return 1;
130
}
131
132
spin_unlock(&mmg->lock);
133
return 0;
134
}
135
136
/**
137
* hl_mmap_mem_buf_alloc - allocate a new mappable buffer
138
*
139
* @mmg: parent unified memory manager
140
* @behavior: behavior object describing this buffer polymorphic behavior
141
* @gfp: gfp flags to use for the memory allocations
142
* @args: additional args passed to behavior->alloc
143
*
144
* Allocate and register a new memory buffer inside the give memory manager.
145
* Return the pointer to the new buffer on success or NULL on failure.
146
*/
147
struct hl_mmap_mem_buf *
148
hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
149
struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp,
150
void *args)
151
{
152
struct hl_mmap_mem_buf *buf;
153
int rc;
154
155
buf = kzalloc(sizeof(*buf), gfp);
156
if (!buf)
157
return NULL;
158
159
spin_lock(&mmg->lock);
160
rc = idr_alloc(&mmg->handles, buf, 1, 0, GFP_ATOMIC);
161
spin_unlock(&mmg->lock);
162
if (rc < 0) {
163
dev_err(mmg->dev,
164
"%s: Failed to allocate IDR for a new buffer, rc=%d\n",
165
behavior->topic, rc);
166
goto free_buf;
167
}
168
169
buf->mmg = mmg;
170
buf->behavior = behavior;
171
buf->handle = (((u64)rc | buf->behavior->mem_id) << PAGE_SHIFT);
172
kref_init(&buf->refcount);
173
174
rc = buf->behavior->alloc(buf, gfp, args);
175
if (rc) {
176
dev_err(mmg->dev, "%s: Failure in buffer alloc callback %d\n",
177
behavior->topic, rc);
178
goto remove_idr;
179
}
180
181
return buf;
182
183
remove_idr:
184
spin_lock(&mmg->lock);
185
idr_remove(&mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
186
spin_unlock(&mmg->lock);
187
free_buf:
188
kfree(buf);
189
return NULL;
190
}
191
192
/**
193
* hl_mmap_mem_buf_vm_close - handle mmap close
194
*
195
* @vma: the vma object for which mmap was closed.
196
*
197
* Put the memory buffer if it is no longer mapped.
198
*/
199
static void hl_mmap_mem_buf_vm_close(struct vm_area_struct *vma)
200
{
201
struct hl_mmap_mem_buf *buf =
202
(struct hl_mmap_mem_buf *)vma->vm_private_data;
203
long new_mmap_size;
204
205
new_mmap_size = buf->real_mapped_size - (vma->vm_end - vma->vm_start);
206
207
if (new_mmap_size > 0) {
208
buf->real_mapped_size = new_mmap_size;
209
return;
210
}
211
212
atomic_set(&buf->mmap, 0);
213
hl_mmap_mem_buf_put(buf);
214
vma->vm_private_data = NULL;
215
}
216
217
static const struct vm_operations_struct hl_mmap_mem_buf_vm_ops = {
218
.close = hl_mmap_mem_buf_vm_close
219
};
220
221
/**
222
* hl_mem_mgr_mmap - map the given buffer to the user
223
*
224
* @mmg: unified memory manager
225
* @vma: the vma object for which mmap was closed.
226
* @args: additional args passed to behavior->mmap
227
*
228
* Map the buffer specified by the vma->vm_pgoff to the given vma.
229
*/
230
int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
231
void *args)
232
{
233
struct hl_mmap_mem_buf *buf;
234
u64 user_mem_size;
235
u64 handle;
236
int rc;
237
238
/* We use the page offset to hold the idr and thus we need to clear
239
* it before doing the mmap itself
240
*/
241
handle = vma->vm_pgoff << PAGE_SHIFT;
242
vma->vm_pgoff = 0;
243
244
/* Reference was taken here */
245
buf = hl_mmap_mem_buf_get(mmg, handle);
246
if (!buf) {
247
dev_err(mmg->dev,
248
"Memory mmap failed, no match to handle %#llx\n", handle);
249
return -EINVAL;
250
}
251
252
/* Validation check */
253
user_mem_size = vma->vm_end - vma->vm_start;
254
if (user_mem_size != ALIGN(buf->mappable_size, PAGE_SIZE)) {
255
dev_err(mmg->dev,
256
"%s: Memory mmap failed, mmap VM size 0x%llx != 0x%llx allocated physical mem size\n",
257
buf->behavior->topic, user_mem_size, buf->mappable_size);
258
rc = -EINVAL;
259
goto put_mem;
260
}
261
262
#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK
263
if (!access_ok(VERIFY_WRITE, (void __user *)(uintptr_t)vma->vm_start,
264
user_mem_size)) {
265
#else
266
if (!access_ok((void __user *)(uintptr_t)vma->vm_start,
267
user_mem_size)) {
268
#endif
269
dev_err(mmg->dev, "%s: User pointer is invalid - 0x%lx\n",
270
buf->behavior->topic, vma->vm_start);
271
272
rc = -EINVAL;
273
goto put_mem;
274
}
275
276
if (atomic_cmpxchg(&buf->mmap, 0, 1)) {
277
dev_err(mmg->dev,
278
"%s, Memory mmap failed, already mapped to user\n",
279
buf->behavior->topic);
280
rc = -EINVAL;
281
goto put_mem;
282
}
283
284
vma->vm_ops = &hl_mmap_mem_buf_vm_ops;
285
286
/* Note: We're transferring the memory reference to vma->vm_private_data here. */
287
288
vma->vm_private_data = buf;
289
290
rc = buf->behavior->mmap(buf, vma, args);
291
if (rc) {
292
atomic_set(&buf->mmap, 0);
293
goto put_mem;
294
}
295
296
buf->real_mapped_size = buf->mappable_size;
297
vma->vm_pgoff = handle >> PAGE_SHIFT;
298
299
return 0;
300
301
put_mem:
302
hl_mmap_mem_buf_put(buf);
303
return rc;
304
}
305
306
/**
307
* hl_mem_mgr_init - initialize unified memory manager
308
*
309
* @dev: owner device pointer
310
* @mmg: structure to initialize
311
*
312
* Initialize an instance of unified memory manager
313
*/
314
void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg)
315
{
316
mmg->dev = dev;
317
spin_lock_init(&mmg->lock);
318
idr_init(&mmg->handles);
319
}
320
321
static void hl_mem_mgr_fini_stats_reset(struct hl_mem_mgr_fini_stats *stats)
322
{
323
if (!stats)
324
return;
325
326
memset(stats, 0, sizeof(*stats));
327
}
328
329
static void hl_mem_mgr_fini_stats_inc(u64 mem_id, struct hl_mem_mgr_fini_stats *stats)
330
{
331
if (!stats)
332
return;
333
334
switch (mem_id) {
335
case HL_MMAP_TYPE_CB:
336
++stats->n_busy_cb;
337
break;
338
case HL_MMAP_TYPE_TS_BUFF:
339
++stats->n_busy_ts;
340
break;
341
default:
342
/* we currently store only CB/TS so this shouldn't happen */
343
++stats->n_busy_other;
344
}
345
}
346
347
/**
348
* hl_mem_mgr_fini - release unified memory manager
349
*
350
* @mmg: parent unified memory manager
351
* @stats: if non-NULL, will return some counters for handles that could not be removed.
352
*
353
* Release the unified memory manager. Shall be called from an interrupt context.
354
*/
355
void hl_mem_mgr_fini(struct hl_mem_mgr *mmg, struct hl_mem_mgr_fini_stats *stats)
356
{
357
struct hl_mmap_mem_buf *buf;
358
struct idr *idp;
359
const char *topic;
360
u64 mem_id;
361
u32 id;
362
363
hl_mem_mgr_fini_stats_reset(stats);
364
365
idp = &mmg->handles;
366
367
idr_for_each_entry(idp, buf, id) {
368
topic = buf->behavior->topic;
369
mem_id = buf->behavior->mem_id;
370
if (hl_mmap_mem_buf_put(buf) != 1) {
371
dev_err(mmg->dev,
372
"%s: Buff handle %u for CTX is still alive\n",
373
topic, id);
374
hl_mem_mgr_fini_stats_inc(mem_id, stats);
375
}
376
}
377
}
378
379
/**
380
* hl_mem_mgr_idr_destroy() - destroy memory manager IDR.
381
* @mmg: parent unified memory manager
382
*
383
* Destroy the memory manager IDR.
384
* Shall be called when IDR is empty and no memory buffers are in use.
385
*/
386
void hl_mem_mgr_idr_destroy(struct hl_mem_mgr *mmg)
387
{
388
if (!idr_is_empty(&mmg->handles))
389
dev_crit(mmg->dev, "memory manager IDR is destroyed while it is not empty!\n");
390
391
idr_destroy(&mmg->handles);
392
}
393
394