diff options
Diffstat (limited to 'drivers/gpu/nvgpu/common/semaphore.c')
-rw-r--r-- | drivers/gpu/nvgpu/common/semaphore.c | 469 |
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 | */ | ||
52 | struct nvgpu_semaphore_sea *nvgpu_semaphore_get_sea(struct gk20a *g) | ||
53 | { | ||
54 | return g->sema_sea; | ||
55 | } | ||
56 | |||
57 | static 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 | |||
73 | out: | ||
74 | __unlock_sema_sea(sea); | ||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | void 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 | */ | ||
93 | struct 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 | |||
115 | cleanup_destroy: | ||
116 | nvgpu_mutex_destroy(&g->sema_sea->sea_lock); | ||
117 | cleanup_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 | |||
124 | static 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 | */ | ||
139 | struct 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 | |||
180 | fail_alloc: | ||
181 | nvgpu_mutex_destroy(&p->pool_lock); | ||
182 | fail: | ||
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 | */ | ||
193 | int 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 | |||
261 | fail_free_submem: | ||
262 | nvgpu_dma_free(pool_to_gk20a(p), &p->rw_mem); | ||
263 | fail_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); | ||
267 | fail_unlock: | ||
268 | __unlock_sema_sea(p->sema_sea); | ||
269 | return err; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * Unmap a semaphore_pool. | ||
274 | */ | ||
275 | void 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 | */ | ||
298 | static 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 | |||
325 | void nvgpu_semaphore_pool_get(struct nvgpu_semaphore_pool *p) | ||
326 | { | ||
327 | nvgpu_ref_get(&p->ref); | ||
328 | } | ||
329 | |||
330 | void 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 | */ | ||
339 | u64 __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 | |||
347 | static 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 | |||
387 | fail_free_idx: | ||
388 | clear_bit(hw_sema_idx, p->semas_alloced); | ||
389 | fail: | ||
390 | nvgpu_mutex_release(&p->pool_lock); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * Free the channel used semaphore index | ||
396 | */ | ||
397 | void 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 | */ | ||
421 | struct 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 | |||
451 | static 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 | |||
461 | void nvgpu_semaphore_put(struct nvgpu_semaphore *s) | ||
462 | { | ||
463 | nvgpu_ref_put(&s->ref, nvgpu_semaphore_free); | ||
464 | } | ||
465 | |||
466 | void nvgpu_semaphore_get(struct nvgpu_semaphore *s) | ||
467 | { | ||
468 | nvgpu_ref_get(&s->ref); | ||
469 | } | ||