summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/mm/lockless_allocator.c
diff options
context:
space:
mode:
authorAlex Waterman <alexw@nvidia.com>2016-12-20 16:55:48 -0500
committermobile promotions <svcmobile_promotions@nvidia.com>2017-01-09 15:33:16 -0500
commit6df3992b60959d32c7113cb77e131a2547174f3a (patch)
treeefbdc9e6ccd2330d5c469ca0783ecb0137da8fc4 /drivers/gpu/nvgpu/common/mm/lockless_allocator.c
parente229514bece5a109cdbfe263f6329efe987e5939 (diff)
gpu: nvgpu: Move allocators to common/mm/
Move the GPU allocators to common/mm/ since the allocators are common code across all GPUs. Also rename the allocator code to move away from gk20a_ prefixed structs and functions. This caused one issue with the nvgpu_alloc() and nvgpu_free() functions. There was a function for allocating either with kmalloc() or vmalloc() depending on the size of the allocation. Those have now been renamed to nvgpu_kalloc() and nvgpu_kfree(). Bug 1799159 Change-Id: Iddda92c013612bcb209847084ec85b8953002fa5 Signed-off-by: Alex Waterman <alexw@nvidia.com> Reviewed-on: http://git-master/r/1274400 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/common/mm/lockless_allocator.c')
-rw-r--r--drivers/gpu/nvgpu/common/mm/lockless_allocator.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/mm/lockless_allocator.c b/drivers/gpu/nvgpu/common/mm/lockless_allocator.c
new file mode 100644
index 00000000..e3063a42
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/mm/lockless_allocator.c
@@ -0,0 +1,207 @@
1/*
2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <linux/kernel.h>
18#include <linux/slab.h>
19#include <linux/vmalloc.h>
20#include <linux/atomic.h>
21
22#include <nvgpu/allocator.h>
23
24#include "lockless_allocator_priv.h"
25
26static u64 nvgpu_lockless_alloc_length(struct nvgpu_allocator *a)
27{
28 struct nvgpu_lockless_allocator *pa = a->priv;
29
30 return pa->length;
31}
32
33static u64 nvgpu_lockless_alloc_base(struct nvgpu_allocator *a)
34{
35 struct nvgpu_lockless_allocator *pa = a->priv;
36
37 return pa->base;
38}
39
40static int nvgpu_lockless_alloc_inited(struct nvgpu_allocator *a)
41{
42 struct nvgpu_lockless_allocator *pa = a->priv;
43 int inited = pa->inited;
44
45 rmb();
46 return inited;
47}
48
49static u64 nvgpu_lockless_alloc_end(struct nvgpu_allocator *a)
50{
51 struct nvgpu_lockless_allocator *pa = a->priv;
52
53 return pa->base + pa->length;
54}
55
56static u64 nvgpu_lockless_alloc(struct nvgpu_allocator *a, u64 len)
57{
58 struct nvgpu_lockless_allocator *pa = a->priv;
59 int head, new_head, ret;
60 u64 addr = 0;
61
62 if (len != pa->blk_size)
63 return 0;
64
65 head = ACCESS_ONCE(pa->head);
66 while (head >= 0) {
67 new_head = ACCESS_ONCE(pa->next[head]);
68 ret = cmpxchg(&pa->head, head, new_head);
69 if (ret == head) {
70 addr = pa->base + head * pa->blk_size;
71 atomic_inc(&pa->nr_allocs);
72 alloc_dbg(a, "Alloc node # %d @ addr 0x%llx\n", head,
73 addr);
74 break;
75 }
76 head = ACCESS_ONCE(pa->head);
77 }
78 return addr;
79}
80
81static void nvgpu_lockless_free(struct nvgpu_allocator *a, u64 addr)
82{
83 struct nvgpu_lockless_allocator *pa = a->priv;
84 int head, ret;
85 u64 cur_idx, rem;
86
87 cur_idx = addr - pa->base;
88 rem = do_div(cur_idx, pa->blk_size);
89
90 while (1) {
91 head = ACCESS_ONCE(pa->head);
92 ACCESS_ONCE(pa->next[cur_idx]) = head;
93 ret = cmpxchg(&pa->head, head, cur_idx);
94 if (ret == head) {
95 atomic_dec(&pa->nr_allocs);
96 alloc_dbg(a, "Free node # %llu\n", cur_idx);
97 break;
98 }
99 }
100}
101
102static void nvgpu_lockless_alloc_destroy(struct nvgpu_allocator *a)
103{
104 struct nvgpu_lockless_allocator *pa = a->priv;
105
106 nvgpu_fini_alloc_debug(a);
107
108 vfree(pa->next);
109 kfree(pa);
110}
111
112static void nvgpu_lockless_print_stats(struct nvgpu_allocator *a,
113 struct seq_file *s, int lock)
114{
115 struct nvgpu_lockless_allocator *pa = a->priv;
116
117 __alloc_pstat(s, a, "Lockless allocator params:\n");
118 __alloc_pstat(s, a, " start = 0x%llx\n", pa->base);
119 __alloc_pstat(s, a, " end = 0x%llx\n", pa->base + pa->length);
120
121 /* Actual stats. */
122 __alloc_pstat(s, a, "Stats:\n");
123 __alloc_pstat(s, a, " Number allocs = %d\n",
124 atomic_read(&pa->nr_allocs));
125 __alloc_pstat(s, a, " Number free = %d\n",
126 pa->nr_nodes - atomic_read(&pa->nr_allocs));
127}
128
129static const struct nvgpu_allocator_ops pool_ops = {
130 .alloc = nvgpu_lockless_alloc,
131 .free = nvgpu_lockless_free,
132
133 .base = nvgpu_lockless_alloc_base,
134 .length = nvgpu_lockless_alloc_length,
135 .end = nvgpu_lockless_alloc_end,
136 .inited = nvgpu_lockless_alloc_inited,
137
138 .fini = nvgpu_lockless_alloc_destroy,
139
140 .print_stats = nvgpu_lockless_print_stats,
141};
142
143int nvgpu_lockless_allocator_init(struct gk20a *g, struct nvgpu_allocator *__a,
144 const char *name, u64 base, u64 length,
145 u64 blk_size, u64 flags)
146{
147 int i;
148 int err;
149 int nr_nodes;
150 u64 count, rem;
151 struct nvgpu_lockless_allocator *a;
152
153 if (!blk_size)
154 return -EINVAL;
155
156 /*
157 * Ensure we have space for atleast one node & there's no overflow.
158 * In order to control memory footprint, we require count < INT_MAX
159 */
160 count = length;
161 rem = do_div(count, blk_size);
162 if (!base || !count || count > INT_MAX)
163 return -EINVAL;
164
165 a = kzalloc(sizeof(struct nvgpu_lockless_allocator), GFP_KERNEL);
166 if (!a)
167 return -ENOMEM;
168
169 err = __nvgpu_alloc_common_init(__a, name, a, false, &pool_ops);
170 if (err)
171 goto fail;
172
173 a->next = vzalloc(sizeof(*a->next) * count);
174 if (!a->next) {
175 err = -ENOMEM;
176 goto fail;
177 }
178
179 /* chain the elements together to form the initial free list */
180 nr_nodes = (int)count;
181 for (i = 0; i < nr_nodes; i++)
182 a->next[i] = i + 1;
183 a->next[nr_nodes - 1] = -1;
184
185 a->base = base;
186 a->length = length;
187 a->blk_size = blk_size;
188 a->nr_nodes = nr_nodes;
189 a->flags = flags;
190 atomic_set(&a->nr_allocs, 0);
191
192 wmb();
193 a->inited = true;
194
195 nvgpu_init_alloc_debug(g, __a);
196 alloc_dbg(__a, "New allocator: type lockless\n");
197 alloc_dbg(__a, " base 0x%llx\n", a->base);
198 alloc_dbg(__a, " nodes %d\n", a->nr_nodes);
199 alloc_dbg(__a, " blk_size 0x%llx\n", a->blk_size);
200 alloc_dbg(__a, " flags 0x%llx\n", a->flags);
201
202 return 0;
203
204fail:
205 kfree(a);
206 return err;
207}