summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/semaphore.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/semaphore.c')
-rw-r--r--drivers/gpu/nvgpu/common/semaphore.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/semaphore.c b/drivers/gpu/nvgpu/common/semaphore.c
new file mode 100644
index 00000000..57e3e1d1
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/semaphore.c
@@ -0,0 +1,469 @@
1/*
2 * Nvgpu Semaphores
3 *
4 * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25#include <nvgpu/dma.h>
26#include <nvgpu/gmmu.h>
27#include <nvgpu/semaphore.h>
28#include <nvgpu/kmem.h>
29#include <nvgpu/bug.h>
30
31#include "gk20a/gk20a.h"
32#include "gk20a/mm_gk20a.h"
33
34#define pool_to_gk20a(p) ((p)->sema_sea->gk20a)
35
36#define __lock_sema_sea(s) \
37 do { \
38 gpu_sema_verbose_dbg(s->gk20a, "Acquiring sema lock..."); \
39 nvgpu_mutex_acquire(&s->sea_lock); \
40 gpu_sema_verbose_dbg(s->gk20a, "Sema lock aquried!"); \
41 } while (0)
42
43#define __unlock_sema_sea(s) \
44 do { \
45 nvgpu_mutex_release(&s->sea_lock); \
46 gpu_sema_verbose_dbg(s->gk20a, "Released sema lock"); \
47 } while (0)
48
49/*
50 * Return the sema_sea pointer.
51 */
52struct nvgpu_semaphore_sea *nvgpu_semaphore_get_sea(struct gk20a *g)
53{
54 return g->sema_sea;
55}
56
57static int __nvgpu_semaphore_sea_grow(struct nvgpu_semaphore_sea *sea)
58{
59 int ret = 0;
60 struct gk20a *gk20a = sea->gk20a;
61
62 __lock_sema_sea(sea);
63
64 ret = nvgpu_dma_alloc_sys(gk20a,
65 PAGE_SIZE * SEMAPHORE_POOL_COUNT,
66 &sea->sea_mem);
67 if (ret)
68 goto out;
69
70 sea->size = SEMAPHORE_POOL_COUNT;
71 sea->map_size = SEMAPHORE_POOL_COUNT * PAGE_SIZE;
72
73out:
74 __unlock_sema_sea(sea);
75 return ret;
76}
77
78void nvgpu_semaphore_sea_destroy(struct gk20a *g)
79{
80 if (!g->sema_sea)
81 return;
82
83 nvgpu_dma_free(g, &g->sema_sea->sea_mem);
84 nvgpu_mutex_destroy(&g->sema_sea->sea_lock);
85 nvgpu_kfree(g, g->sema_sea);
86 g->sema_sea = NULL;
87}
88
89/*
90 * Create the semaphore sea. Only create it once - subsequent calls to this will
91 * return the originally created sea pointer.
92 */
93struct nvgpu_semaphore_sea *nvgpu_semaphore_sea_create(struct gk20a *g)
94{
95 if (g->sema_sea)
96 return g->sema_sea;
97
98 g->sema_sea = nvgpu_kzalloc(g, sizeof(*g->sema_sea));
99 if (!g->sema_sea)
100 return NULL;
101
102 g->sema_sea->size = 0;
103 g->sema_sea->page_count = 0;
104 g->sema_sea->gk20a = g;
105 nvgpu_init_list_node(&g->sema_sea->pool_list);
106 if (nvgpu_mutex_init(&g->sema_sea->sea_lock))
107 goto cleanup_free;
108
109 if (__nvgpu_semaphore_sea_grow(g->sema_sea))
110 goto cleanup_destroy;
111
112 gpu_sema_dbg(g, "Created semaphore sea!");
113 return g->sema_sea;
114
115cleanup_destroy:
116 nvgpu_mutex_destroy(&g->sema_sea->sea_lock);
117cleanup_free:
118 nvgpu_kfree(g, g->sema_sea);
119 g->sema_sea = NULL;
120 gpu_sema_dbg(g, "Failed to creat semaphore sea!");
121 return NULL;
122}
123
124static int __semaphore_bitmap_alloc(unsigned long *bitmap, unsigned long len)
125{
126 unsigned long idx = find_first_zero_bit(bitmap, len);
127
128 if (idx == len)
129 return -ENOSPC;
130
131 set_bit(idx, bitmap);
132
133 return (int)idx;
134}
135
136/*
137 * Allocate a pool from the sea.
138 */
139struct nvgpu_semaphore_pool *nvgpu_semaphore_pool_alloc(
140 struct nvgpu_semaphore_sea *sea)
141{
142 struct nvgpu_semaphore_pool *p;
143 unsigned long page_idx;
144 int ret, err = 0;
145
146 p = nvgpu_kzalloc(sea->gk20a, sizeof(*p));
147 if (!p)
148 return ERR_PTR(-ENOMEM);
149
150 __lock_sema_sea(sea);
151
152 err = nvgpu_mutex_init(&p->pool_lock);
153 if (err)
154 goto fail;
155
156 ret = __semaphore_bitmap_alloc(sea->pools_alloced,
157 SEMAPHORE_POOL_COUNT);
158 if (ret < 0) {
159 err = ret;
160 goto fail_alloc;
161 }
162
163 page_idx = (unsigned long)ret;
164
165 p->page_idx = page_idx;
166 p->sema_sea = sea;
167 nvgpu_init_list_node(&p->hw_semas);
168 nvgpu_init_list_node(&p->pool_list_entry);
169 nvgpu_ref_init(&p->ref);
170
171 sea->page_count++;
172 nvgpu_list_add(&p->pool_list_entry, &sea->pool_list);
173 __unlock_sema_sea(sea);
174
175 gpu_sema_dbg(sea->gk20a,
176 "Allocated semaphore pool: page-idx=%d", p->page_idx);
177
178 return p;
179
180fail_alloc:
181 nvgpu_mutex_destroy(&p->pool_lock);
182fail:
183 __unlock_sema_sea(sea);
184 nvgpu_kfree(sea->gk20a, p);
185 gpu_sema_dbg(sea->gk20a, "Failed to allocate semaphore pool!");
186 return ERR_PTR(err);
187}
188
189/*
190 * Map a pool into the passed vm's address space. This handles both the fixed
191 * global RO mapping and the non-fixed private RW mapping.
192 */
193int nvgpu_semaphore_pool_map(struct nvgpu_semaphore_pool *p,
194 struct vm_gk20a *vm)
195{
196 int err = 0;
197 u64 addr;
198
199 if (p->mapped)
200 return -EBUSY;
201
202 gpu_sema_dbg(pool_to_gk20a(p),
203 "Mapping semaphore pool! (idx=%d)", p->page_idx);
204
205 /*
206 * Take the sea lock so that we don't race with a possible change to the
207 * nvgpu_mem in the sema sea.
208 */
209 __lock_sema_sea(p->sema_sea);
210
211 addr = nvgpu_gmmu_map_fixed(vm, &p->sema_sea->sea_mem,
212 p->sema_sea->gpu_va,
213 p->sema_sea->map_size,
214 0, gk20a_mem_flag_read_only, 0,
215 p->sema_sea->sea_mem.aperture);
216 if (!addr) {
217 err = -ENOMEM;
218 goto fail_unlock;
219 }
220
221 p->gpu_va_ro = addr;
222 p->mapped = 1;
223
224 gpu_sema_dbg(pool_to_gk20a(p),
225 " %d: GPU read-only VA = 0x%llx",
226 p->page_idx, p->gpu_va_ro);
227
228 /*
229 * Now the RW mapping. This is a bit more complicated. We make a
230 * nvgpu_mem describing a page of the bigger RO space and then map
231 * that. Unlike above this does not need to be a fixed address.
232 */
233 err = nvgpu_mem_create_from_mem(vm->mm->g,
234 &p->rw_mem, &p->sema_sea->sea_mem,
235 p->page_idx, 1);
236 if (err)
237 goto fail_unmap;
238
239 addr = nvgpu_gmmu_map(vm, &p->rw_mem, SZ_4K, 0,
240 gk20a_mem_flag_none, 0,
241 p->rw_mem.aperture);
242
243 if (!addr) {
244 err = -ENOMEM;
245 goto fail_free_submem;
246 }
247
248 p->gpu_va = addr;
249
250 __unlock_sema_sea(p->sema_sea);
251
252 gpu_sema_dbg(pool_to_gk20a(p),
253 " %d: GPU read-write VA = 0x%llx",
254 p->page_idx, p->gpu_va);
255 gpu_sema_dbg(pool_to_gk20a(p),
256 " %d: CPU VA = 0x%p",
257 p->page_idx, p->rw_mem.cpu_va);
258
259 return 0;
260
261fail_free_submem:
262 nvgpu_dma_free(pool_to_gk20a(p), &p->rw_mem);
263fail_unmap:
264 nvgpu_gmmu_unmap(vm, &p->sema_sea->sea_mem, p->gpu_va_ro);
265 gpu_sema_dbg(pool_to_gk20a(p),
266 " %d: Failed to map semaphore pool!", p->page_idx);
267fail_unlock:
268 __unlock_sema_sea(p->sema_sea);
269 return err;
270}
271
272/*
273 * Unmap a semaphore_pool.
274 */
275void nvgpu_semaphore_pool_unmap(struct nvgpu_semaphore_pool *p,
276 struct vm_gk20a *vm)
277{
278 __lock_sema_sea(p->sema_sea);
279
280 nvgpu_gmmu_unmap(vm, &p->sema_sea->sea_mem, p->gpu_va_ro);
281 nvgpu_gmmu_unmap(vm, &p->rw_mem, p->gpu_va);
282 nvgpu_dma_free(pool_to_gk20a(p), &p->rw_mem);
283
284 p->gpu_va = 0;
285 p->gpu_va_ro = 0;
286 p->mapped = 0;
287
288 __unlock_sema_sea(p->sema_sea);
289
290 gpu_sema_dbg(pool_to_gk20a(p),
291 "Unmapped semaphore pool! (idx=%d)", p->page_idx);
292}
293
294/*
295 * Completely free a semaphore_pool. You should make sure this pool is not
296 * mapped otherwise there's going to be a memory leak.
297 */
298static void nvgpu_semaphore_pool_free(struct nvgpu_ref *ref)
299{
300 struct nvgpu_semaphore_pool *p =
301 container_of(ref, struct nvgpu_semaphore_pool, ref);
302 struct nvgpu_semaphore_sea *s = p->sema_sea;
303 struct nvgpu_semaphore_int *hw_sema, *tmp;
304
305 /* Freeing a mapped pool is a bad idea. */
306 WARN_ON(p->mapped || p->gpu_va || p->gpu_va_ro);
307
308 __lock_sema_sea(s);
309 nvgpu_list_del(&p->pool_list_entry);
310 clear_bit(p->page_idx, s->pools_alloced);
311 s->page_count--;
312 __unlock_sema_sea(s);
313
314 nvgpu_list_for_each_entry_safe(hw_sema, tmp, &p->hw_semas,
315 nvgpu_semaphore_int, hw_sema_list)
316 nvgpu_kfree(p->sema_sea->gk20a, hw_sema);
317
318 nvgpu_mutex_destroy(&p->pool_lock);
319
320 gpu_sema_dbg(pool_to_gk20a(p),
321 "Freed semaphore pool! (idx=%d)", p->page_idx);
322 nvgpu_kfree(p->sema_sea->gk20a, p);
323}
324
325void nvgpu_semaphore_pool_get(struct nvgpu_semaphore_pool *p)
326{
327 nvgpu_ref_get(&p->ref);
328}
329
330void nvgpu_semaphore_pool_put(struct nvgpu_semaphore_pool *p)
331{
332 nvgpu_ref_put(&p->ref, nvgpu_semaphore_pool_free);
333}
334
335/*
336 * Get the address for a semaphore_pool - if global is true then return the
337 * global RO address instead of the RW address owned by the semaphore's VM.
338 */
339u64 __nvgpu_semaphore_pool_gpu_va(struct nvgpu_semaphore_pool *p, bool global)
340{
341 if (!global)
342 return p->gpu_va;
343
344 return p->gpu_va_ro + (PAGE_SIZE * p->page_idx);
345}
346
347static int __nvgpu_init_hw_sema(struct channel_gk20a *ch)
348{
349 int hw_sema_idx;
350 int ret = 0;
351 struct nvgpu_semaphore_int *hw_sema;
352 struct nvgpu_semaphore_pool *p = ch->vm->sema_pool;
353
354 BUG_ON(!p);
355
356 nvgpu_mutex_acquire(&p->pool_lock);
357
358 /* Find an available HW semaphore. */
359 hw_sema_idx = __semaphore_bitmap_alloc(p->semas_alloced,
360 PAGE_SIZE / SEMAPHORE_SIZE);
361 if (hw_sema_idx < 0) {
362 ret = hw_sema_idx;
363 goto fail;
364 }
365
366 hw_sema = nvgpu_kzalloc(ch->g, sizeof(struct nvgpu_semaphore_int));
367 if (!hw_sema) {
368 ret = -ENOMEM;
369 goto fail_free_idx;
370 }
371
372 ch->hw_sema = hw_sema;
373 hw_sema->ch = ch;
374 hw_sema->p = p;
375 hw_sema->idx = hw_sema_idx;
376 hw_sema->offset = SEMAPHORE_SIZE * hw_sema_idx;
377 nvgpu_atomic_set(&hw_sema->next_value, 0);
378 nvgpu_init_list_node(&hw_sema->hw_sema_list);
379 nvgpu_mem_wr(ch->g, &p->rw_mem, hw_sema->offset, 0);
380
381 nvgpu_list_add(&hw_sema->hw_sema_list, &p->hw_semas);
382
383 nvgpu_mutex_release(&p->pool_lock);
384
385 return 0;
386
387fail_free_idx:
388 clear_bit(hw_sema_idx, p->semas_alloced);
389fail:
390 nvgpu_mutex_release(&p->pool_lock);
391 return ret;
392}
393
394/*
395 * Free the channel used semaphore index
396 */
397void nvgpu_semaphore_free_hw_sema(struct channel_gk20a *ch)
398{
399 struct nvgpu_semaphore_pool *p = ch->vm->sema_pool;
400
401 BUG_ON(!p);
402
403 nvgpu_mutex_acquire(&p->pool_lock);
404
405 clear_bit(ch->hw_sema->idx, p->semas_alloced);
406
407 /* Make sure that when the ch is re-opened it will get a new HW sema. */
408 nvgpu_list_del(&ch->hw_sema->hw_sema_list);
409 nvgpu_kfree(ch->g, ch->hw_sema);
410 ch->hw_sema = NULL;
411
412 nvgpu_mutex_release(&p->pool_lock);
413}
414
415/*
416 * Allocate a semaphore from the passed pool.
417 *
418 * Since semaphores are ref-counted there's no explicit free for external code
419 * to use. When the ref-count hits 0 the internal free will happen.
420 */
421struct nvgpu_semaphore *nvgpu_semaphore_alloc(struct channel_gk20a *ch)
422{
423 struct nvgpu_semaphore *s;
424 int ret;
425
426 if (!ch->hw_sema) {
427 ret = __nvgpu_init_hw_sema(ch);
428 if (ret)
429 return NULL;
430 }
431
432 s = nvgpu_kzalloc(ch->g, sizeof(*s));
433 if (!s)
434 return NULL;
435
436 nvgpu_ref_init(&s->ref);
437 s->hw_sema = ch->hw_sema;
438 nvgpu_atomic_set(&s->value, 0);
439
440 /*
441 * Take a ref on the pool so that we can keep this pool alive for
442 * as long as this semaphore is alive.
443 */
444 nvgpu_semaphore_pool_get(s->hw_sema->p);
445
446 gpu_sema_dbg(ch->g, "Allocated semaphore (c=%d)", ch->chid);
447
448 return s;
449}
450
451static void nvgpu_semaphore_free(struct nvgpu_ref *ref)
452{
453 struct nvgpu_semaphore *s =
454 container_of(ref, struct nvgpu_semaphore, ref);
455
456 nvgpu_semaphore_pool_put(s->hw_sema->p);
457
458 nvgpu_kfree(s->hw_sema->ch->g, s);
459}
460
461void nvgpu_semaphore_put(struct nvgpu_semaphore *s)
462{
463 nvgpu_ref_put(&s->ref, nvgpu_semaphore_free);
464}
465
466void nvgpu_semaphore_get(struct nvgpu_semaphore *s)
467{
468 nvgpu_ref_get(&s->ref);
469}