freedreno: Mark all ringbuffer BOs as to be dumped on crash.
[mesa.git] / src / freedreno / drm / msm_ringbuffer_sp.c
1 /*
2 * Copyright (C) 2018 Rob Clark <robclark@freedesktop.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 * Rob Clark <robclark@freedesktop.org>
25 */
26
27 #include <assert.h>
28 #include <inttypes.h>
29
30 #include "util/hash_table.h"
31 #include "util/slab.h"
32
33 #include "drm/freedreno_ringbuffer.h"
34 #include "msm_priv.h"
35
36 /* A "softpin" implementation of submit/ringbuffer, which lowers CPU overhead
37 * by avoiding the additional tracking necessary to build cmds/relocs tables
38 * (but still builds a bos table)
39 */
40
41
42 #define INIT_SIZE 0x1000
43
44
45 struct msm_submit_sp {
46 struct fd_submit base;
47
48 DECLARE_ARRAY(struct drm_msm_gem_submit_bo, submit_bos);
49 DECLARE_ARRAY(struct fd_bo *, bos);
50
51 /* maps fd_bo to idx in bos table: */
52 struct hash_table *bo_table;
53
54 struct slab_child_pool ring_pool;
55
56 struct fd_ringbuffer *primary;
57
58 /* Allow for sub-allocation of stateobj ring buffers (ie. sharing
59 * the same underlying bo)..
60 *
61 * We also rely on previous stateobj having been fully constructed
62 * so we can reclaim extra space at it's end.
63 */
64 struct fd_ringbuffer *suballoc_ring;
65 };
66 FD_DEFINE_CAST(fd_submit, msm_submit_sp);
67
68 /* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers
69 * and sizes. Ie. a finalized buffer can have no more commands appended to
70 * it.
71 */
72 struct msm_cmd_sp {
73 struct fd_bo *ring_bo;
74 unsigned size;
75 };
76
77 /* for _FD_RINGBUFFER_OBJECT rb's we need to track the bo's and flags to
78 * later copy into the submit when the stateobj rb is later referenced by
79 * a regular rb:
80 */
81 struct msm_reloc_bo_sp {
82 struct fd_bo *bo;
83 unsigned flags;
84 };
85
86 struct msm_ringbuffer_sp {
87 struct fd_ringbuffer base;
88
89 /* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */
90 unsigned offset;
91
92 // TODO check disasm.. hopefully compilers CSE can realize that
93 // reloc_bos and cmds are at the same offsets and optimize some
94 // divergent cases into single case
95 union {
96 /* for _FD_RINGBUFFER_OBJECT case: */
97 struct {
98 struct fd_pipe *pipe;
99 DECLARE_ARRAY(struct msm_reloc_bo_sp, reloc_bos);
100 };
101 /* for other cases: */
102 struct {
103 struct fd_submit *submit;
104 DECLARE_ARRAY(struct msm_cmd_sp, cmds);
105 };
106 } u;
107
108 struct fd_bo *ring_bo;
109 };
110 FD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer_sp);
111
112 static void finalize_current_cmd(struct fd_ringbuffer *ring);
113 static struct fd_ringbuffer * msm_ringbuffer_sp_init(
114 struct msm_ringbuffer_sp *msm_ring,
115 uint32_t size, enum fd_ringbuffer_flags flags);
116
117 /* add (if needed) bo to submit and return index: */
118 static uint32_t
119 msm_submit_append_bo(struct msm_submit_sp *submit, struct fd_bo *bo, uint32_t flags)
120 {
121 struct msm_bo *msm_bo = to_msm_bo(bo);
122 uint32_t idx;
123
124 /* NOTE: it is legal to use the same bo on different threads for
125 * different submits. But it is not legal to use the same submit
126 * from given threads.
127 */
128 idx = READ_ONCE(msm_bo->idx);
129
130 if (unlikely((idx >= submit->nr_submit_bos) ||
131 (submit->submit_bos[idx].handle != bo->handle))) {
132 uint32_t hash = _mesa_hash_pointer(bo);
133 struct hash_entry *entry;
134
135 entry = _mesa_hash_table_search_pre_hashed(submit->bo_table, hash, bo);
136 if (entry) {
137 /* found */
138 idx = (uint32_t)(uintptr_t)entry->data;
139 } else {
140 idx = APPEND(submit, submit_bos);
141 idx = APPEND(submit, bos);
142
143 submit->submit_bos[idx].flags = bo->flags;
144 submit->submit_bos[idx].handle = bo->handle;
145 submit->submit_bos[idx].presumed = 0;
146
147 submit->bos[idx] = fd_bo_ref(bo);
148
149 _mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo,
150 (void *)(uintptr_t)idx);
151 }
152 msm_bo->idx = idx;
153 }
154
155 STATIC_ASSERT(FD_RELOC_READ == MSM_SUBMIT_BO_READ);
156 STATIC_ASSERT(FD_RELOC_WRITE == MSM_SUBMIT_BO_WRITE);
157 STATIC_ASSERT(FD_RELOC_DUMP == MSM_SUBMIT_BO_DUMP);
158 submit->submit_bos[idx].flags |=
159 flags & (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE | MSM_SUBMIT_BO_DUMP);
160
161 return idx;
162 }
163
164 static void
165 msm_submit_suballoc_ring_bo(struct fd_submit *submit,
166 struct msm_ringbuffer_sp *msm_ring, uint32_t size)
167 {
168 struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
169 unsigned suballoc_offset = 0;
170 struct fd_bo *suballoc_bo = NULL;
171
172 if (msm_submit->suballoc_ring) {
173 struct msm_ringbuffer_sp *suballoc_ring =
174 to_msm_ringbuffer_sp(msm_submit->suballoc_ring);
175
176 suballoc_bo = suballoc_ring->ring_bo;
177 suballoc_offset = fd_ringbuffer_size(msm_submit->suballoc_ring) +
178 suballoc_ring->offset;
179
180 suballoc_offset = align(suballoc_offset, 0x10);
181
182 if ((size + suballoc_offset) > suballoc_bo->size) {
183 suballoc_bo = NULL;
184 }
185 }
186
187 if (!suballoc_bo) {
188 // TODO possibly larger size for streaming bo?
189 msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, 0x8000);
190 msm_ring->offset = 0;
191 } else {
192 msm_ring->ring_bo = fd_bo_ref(suballoc_bo);
193 msm_ring->offset = suballoc_offset;
194 }
195
196 struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring;
197
198 msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base);
199
200 if (old_suballoc_ring)
201 fd_ringbuffer_del(old_suballoc_ring);
202 }
203
204 static struct fd_ringbuffer *
205 msm_submit_sp_new_ringbuffer(struct fd_submit *submit, uint32_t size,
206 enum fd_ringbuffer_flags flags)
207 {
208 struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
209 struct msm_ringbuffer_sp *msm_ring;
210
211 msm_ring = slab_alloc(&msm_submit->ring_pool);
212
213 msm_ring->u.submit = submit;
214
215 /* NOTE: needs to be before _suballoc_ring_bo() since it could
216 * increment the refcnt of the current ring
217 */
218 msm_ring->base.refcnt = 1;
219
220 if (flags & FD_RINGBUFFER_STREAMING) {
221 msm_submit_suballoc_ring_bo(submit, msm_ring, size);
222 } else {
223 if (flags & FD_RINGBUFFER_GROWABLE)
224 size = INIT_SIZE;
225
226 msm_ring->offset = 0;
227 msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size);
228 }
229
230 if (!msm_ringbuffer_sp_init(msm_ring, size, flags))
231 return NULL;
232
233 if (flags & FD_RINGBUFFER_PRIMARY) {
234 debug_assert(!msm_submit->primary);
235 msm_submit->primary = fd_ringbuffer_ref(&msm_ring->base);
236 }
237
238 return &msm_ring->base;
239 }
240
241 static int
242 msm_submit_sp_flush(struct fd_submit *submit, int in_fence_fd,
243 int *out_fence_fd, uint32_t *out_fence)
244 {
245 struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
246 struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);
247 struct drm_msm_gem_submit req = {
248 .flags = msm_pipe->pipe,
249 .queueid = msm_pipe->queue_id,
250 };
251 int ret;
252
253 debug_assert(msm_submit->primary);
254 finalize_current_cmd(msm_submit->primary);
255
256 struct msm_ringbuffer_sp *primary = to_msm_ringbuffer_sp(msm_submit->primary);
257 struct drm_msm_gem_submit_cmd cmds[primary->u.nr_cmds];
258
259 for (unsigned i = 0; i < primary->u.nr_cmds; i++) {
260 cmds[i].type = MSM_SUBMIT_CMD_BUF;
261 cmds[i].submit_idx = msm_submit_append_bo(msm_submit,
262 primary->u.cmds[i].ring_bo, 0);
263 cmds[i].submit_offset = primary->offset;
264 cmds[i].size = primary->u.cmds[i].size;
265 cmds[i].pad = 0;
266 cmds[i].nr_relocs = 0;
267 }
268
269 if (in_fence_fd != -1) {
270 req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;
271 req.fence_fd = in_fence_fd;
272 }
273
274 if (out_fence_fd) {
275 req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
276 }
277
278 /* needs to be after get_cmd() as that could create bos/cmds table: */
279 req.bos = VOID2U64(msm_submit->submit_bos),
280 req.nr_bos = msm_submit->nr_submit_bos;
281 req.cmds = VOID2U64(cmds),
282 req.nr_cmds = primary->u.nr_cmds;
283
284 DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
285
286 ret = drmCommandWriteRead(submit->pipe->dev->fd, DRM_MSM_GEM_SUBMIT,
287 &req, sizeof(req));
288 if (ret) {
289 ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
290 msm_dump_submit(&req);
291 } else if (!ret) {
292 if (out_fence)
293 *out_fence = req.fence;
294
295 if (out_fence_fd)
296 *out_fence_fd = req.fence_fd;
297 }
298
299 return ret;
300 }
301
302 static void
303 msm_submit_sp_destroy(struct fd_submit *submit)
304 {
305 struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
306
307 if (msm_submit->primary)
308 fd_ringbuffer_del(msm_submit->primary);
309 if (msm_submit->suballoc_ring)
310 fd_ringbuffer_del(msm_submit->suballoc_ring);
311
312 _mesa_hash_table_destroy(msm_submit->bo_table, NULL);
313
314 // TODO it would be nice to have a way to debug_assert() if all
315 // rb's haven't been free'd back to the slab, because that is
316 // an indication that we are leaking bo's
317 slab_destroy_child(&msm_submit->ring_pool);
318
319 for (unsigned i = 0; i < msm_submit->nr_bos; i++)
320 fd_bo_del(msm_submit->bos[i]);
321
322 free(msm_submit->submit_bos);
323 free(msm_submit->bos);
324 free(msm_submit);
325 }
326
327 static const struct fd_submit_funcs submit_funcs = {
328 .new_ringbuffer = msm_submit_sp_new_ringbuffer,
329 .flush = msm_submit_sp_flush,
330 .destroy = msm_submit_sp_destroy,
331 };
332
333 struct fd_submit *
334 msm_submit_sp_new(struct fd_pipe *pipe)
335 {
336 struct msm_submit_sp *msm_submit = calloc(1, sizeof(*msm_submit));
337 struct fd_submit *submit;
338
339 msm_submit->bo_table = _mesa_hash_table_create(NULL,
340 _mesa_hash_pointer, _mesa_key_pointer_equal);
341
342 slab_create_child(&msm_submit->ring_pool, &to_msm_pipe(pipe)->ring_pool);
343
344 submit = &msm_submit->base;
345 submit->pipe = pipe;
346 submit->funcs = &submit_funcs;
347
348 return submit;
349 }
350
351 void
352 msm_pipe_sp_ringpool_init(struct msm_pipe *msm_pipe)
353 {
354 // TODO tune size:
355 slab_create_parent(&msm_pipe->ring_pool, sizeof(struct msm_ringbuffer_sp), 16);
356 }
357
358 void
359 msm_pipe_sp_ringpool_fini(struct msm_pipe *msm_pipe)
360 {
361 if (msm_pipe->ring_pool.num_elements)
362 slab_destroy_parent(&msm_pipe->ring_pool);
363 }
364
365 static void
366 finalize_current_cmd(struct fd_ringbuffer *ring)
367 {
368 debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));
369
370 struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
371 unsigned idx = APPEND(&msm_ring->u, cmds);
372
373 msm_ring->u.cmds[idx].ring_bo = fd_bo_ref(msm_ring->ring_bo);
374 msm_ring->u.cmds[idx].size = offset_bytes(ring->cur, ring->start);
375 }
376
377 static void
378 msm_ringbuffer_sp_grow(struct fd_ringbuffer *ring, uint32_t size)
379 {
380 struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
381 struct fd_pipe *pipe = msm_ring->u.submit->pipe;
382
383 debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE);
384
385 finalize_current_cmd(ring);
386
387 fd_bo_del(msm_ring->ring_bo);
388 msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);
389
390 ring->start = fd_bo_map(msm_ring->ring_bo);
391 ring->end = &(ring->start[size/4]);
392 ring->cur = ring->start;
393 ring->size = size;
394 }
395
396 static void
397 msm_ringbuffer_sp_emit_reloc(struct fd_ringbuffer *ring,
398 const struct fd_reloc *reloc)
399 {
400 struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
401 struct fd_pipe *pipe;
402
403 if (ring->flags & _FD_RINGBUFFER_OBJECT) {
404 unsigned idx = APPEND(&msm_ring->u, reloc_bos);
405
406 msm_ring->u.reloc_bos[idx].bo = fd_bo_ref(reloc->bo);
407 msm_ring->u.reloc_bos[idx].flags = reloc->flags;
408
409 pipe = msm_ring->u.pipe;
410 } else {
411 struct msm_submit_sp *msm_submit =
412 to_msm_submit_sp(msm_ring->u.submit);
413
414 msm_submit_append_bo(msm_submit, reloc->bo, reloc->flags);
415
416 pipe = msm_ring->u.submit->pipe;
417 }
418
419 uint64_t iova = reloc->bo->iova + reloc->offset;
420 int shift = reloc->shift;
421
422 if (shift < 0)
423 iova >>= -shift;
424 else
425 iova <<= shift;
426
427 uint32_t dword = iova;
428
429 (*ring->cur++) = dword | reloc->or;
430
431 if (pipe->gpu_id >= 500) {
432 dword = iova >> 32;
433 (*ring->cur++) = dword | reloc->orhi;
434 }
435 }
436
437 static uint32_t
438 msm_ringbuffer_sp_emit_reloc_ring(struct fd_ringbuffer *ring,
439 struct fd_ringbuffer *target, uint32_t cmd_idx)
440 {
441 struct msm_ringbuffer_sp *msm_target = to_msm_ringbuffer_sp(target);
442 struct fd_bo *bo;
443 uint32_t size;
444
445 if ((target->flags & FD_RINGBUFFER_GROWABLE) &&
446 (cmd_idx < msm_target->u.nr_cmds)) {
447 bo = msm_target->u.cmds[cmd_idx].ring_bo;
448 size = msm_target->u.cmds[cmd_idx].size;
449 } else {
450 bo = msm_target->ring_bo;
451 size = offset_bytes(target->cur, target->start);
452 }
453
454 msm_ringbuffer_sp_emit_reloc(ring, &(struct fd_reloc){
455 .bo = bo,
456 .offset = msm_target->offset,
457 });
458
459 if (!(target->flags & _FD_RINGBUFFER_OBJECT))
460 return size;
461
462 struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
463
464 if (ring->flags & _FD_RINGBUFFER_OBJECT) {
465 for (unsigned i = 0; i < msm_target->u.nr_reloc_bos; i++) {
466 unsigned idx = APPEND(&msm_ring->u, reloc_bos);
467
468 msm_ring->u.reloc_bos[idx].bo =
469 fd_bo_ref(msm_target->u.reloc_bos[i].bo);
470 msm_ring->u.reloc_bos[idx].flags =
471 msm_target->u.reloc_bos[i].flags;
472 }
473 } else {
474 // TODO it would be nice to know whether we have already
475 // seen this target before. But hopefully we hit the
476 // append_bo() fast path enough for this to not matter:
477 struct msm_submit_sp *msm_submit = to_msm_submit_sp(msm_ring->u.submit);
478
479 for (unsigned i = 0; i < msm_target->u.nr_reloc_bos; i++) {
480 msm_submit_append_bo(msm_submit, msm_target->u.reloc_bos[i].bo,
481 msm_target->u.reloc_bos[i].flags);
482 }
483 }
484
485 return size;
486 }
487
488 static uint32_t
489 msm_ringbuffer_sp_cmd_count(struct fd_ringbuffer *ring)
490 {
491 if (ring->flags & FD_RINGBUFFER_GROWABLE)
492 return to_msm_ringbuffer_sp(ring)->u.nr_cmds + 1;
493 return 1;
494 }
495
496 static void
497 msm_ringbuffer_sp_destroy(struct fd_ringbuffer *ring)
498 {
499 struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);
500
501 fd_bo_del(msm_ring->ring_bo);
502
503 if (ring->flags & _FD_RINGBUFFER_OBJECT) {
504 for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) {
505 fd_bo_del(msm_ring->u.reloc_bos[i].bo);
506 }
507 free(msm_ring->u.reloc_bos);
508
509 free(msm_ring);
510 } else {
511 struct fd_submit *submit = msm_ring->u.submit;
512
513 for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) {
514 fd_bo_del(msm_ring->u.cmds[i].ring_bo);
515 }
516 free(msm_ring->u.cmds);
517
518 slab_free(&to_msm_submit_sp(submit)->ring_pool, msm_ring);
519 }
520 }
521
522 static const struct fd_ringbuffer_funcs ring_funcs = {
523 .grow = msm_ringbuffer_sp_grow,
524 .emit_reloc = msm_ringbuffer_sp_emit_reloc,
525 .emit_reloc_ring = msm_ringbuffer_sp_emit_reloc_ring,
526 .cmd_count = msm_ringbuffer_sp_cmd_count,
527 .destroy = msm_ringbuffer_sp_destroy,
528 };
529
530 static inline struct fd_ringbuffer *
531 msm_ringbuffer_sp_init(struct msm_ringbuffer_sp *msm_ring, uint32_t size,
532 enum fd_ringbuffer_flags flags)
533 {
534 struct fd_ringbuffer *ring = &msm_ring->base;
535
536 debug_assert(msm_ring->ring_bo);
537
538 uint8_t *base = fd_bo_map(msm_ring->ring_bo);
539 ring->start = (void *)(base + msm_ring->offset);
540 ring->end = &(ring->start[size/4]);
541 ring->cur = ring->start;
542
543 ring->size = size;
544 ring->flags = flags;
545
546 ring->funcs = &ring_funcs;
547
548 // TODO initializing these could probably be conditional on flags
549 // since unneed for FD_RINGBUFFER_STAGING case..
550 msm_ring->u.cmds = NULL;
551 msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0;
552
553 msm_ring->u.reloc_bos = NULL;
554 msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0;
555
556 return ring;
557 }
558
559 struct fd_ringbuffer *
560 msm_ringbuffer_sp_new_object(struct fd_pipe *pipe, uint32_t size)
561 {
562 struct msm_ringbuffer_sp *msm_ring = malloc(sizeof(*msm_ring));
563
564 msm_ring->u.pipe = pipe;
565 msm_ring->offset = 0;
566 msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);
567 msm_ring->base.refcnt = 1;
568
569 return msm_ringbuffer_sp_init(msm_ring, size, _FD_RINGBUFFER_OBJECT);
570 }