| /* |
| * Copyright (C) 2012-2018 Rob Clark <robclark@freedesktop.org> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| * Authors: |
| * Rob Clark <robclark@freedesktop.org> |
| */ |
| |
| #include <assert.h> |
| #include <inttypes.h> |
| |
| #include "util/hash_table.h" |
| #include "util/set.h" |
| #include "util/slab.h" |
| |
| #include "drm/freedreno_ringbuffer.h" |
| #include "msm_priv.h" |
| |
| /* The legacy implementation of submit/ringbuffer, which still does the |
| * traditional reloc and cmd tracking |
| */ |
| |
| |
| #define INIT_SIZE 0x1000 |
| |
| |
| struct msm_submit { |
| struct fd_submit base; |
| |
| DECLARE_ARRAY(struct drm_msm_gem_submit_bo, submit_bos); |
| DECLARE_ARRAY(struct fd_bo *, bos); |
| |
| /* maps fd_bo to idx in bos table: */ |
| struct hash_table *bo_table; |
| |
| struct slab_mempool ring_pool; |
| |
| /* hash-set of associated rings: */ |
| struct set *ring_set; |
| |
| struct fd_ringbuffer *primary; |
| |
| /* Allow for sub-allocation of stateobj ring buffers (ie. sharing |
| * the same underlying bo).. |
| * |
| * We also rely on previous stateobj having been fully constructed |
| * so we can reclaim extra space at it's end. |
| */ |
| struct fd_ringbuffer *suballoc_ring; |
| }; |
| FD_DEFINE_CAST(fd_submit, msm_submit); |
| |
| /* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers |
| * and sizes. Ie. a finalized buffer can have no more commands appended to |
| * it. |
| */ |
| struct msm_cmd { |
| struct fd_bo *ring_bo; |
| unsigned size; |
| DECLARE_ARRAY(struct drm_msm_gem_submit_reloc, relocs); |
| }; |
| |
| static struct msm_cmd * |
| cmd_new(struct fd_bo *ring_bo) |
| { |
| struct msm_cmd *cmd = malloc(sizeof(*cmd)); |
| cmd->ring_bo = fd_bo_ref(ring_bo); |
| cmd->size = 0; |
| cmd->nr_relocs = cmd->max_relocs = 0; |
| cmd->relocs = NULL; |
| return cmd; |
| } |
| |
| static void |
| cmd_free(struct msm_cmd *cmd) |
| { |
| fd_bo_del(cmd->ring_bo); |
| free(cmd->relocs); |
| free(cmd); |
| } |
| |
| struct msm_ringbuffer { |
| struct fd_ringbuffer base; |
| |
| /* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */ |
| unsigned offset; |
| |
| union { |
| /* for _FD_RINGBUFFER_OBJECT case: */ |
| struct { |
| struct fd_pipe *pipe; |
| DECLARE_ARRAY(struct fd_bo *, reloc_bos); |
| struct set *ring_set; |
| }; |
| /* for other cases: */ |
| struct { |
| struct fd_submit *submit; |
| DECLARE_ARRAY(struct msm_cmd *, cmds); |
| }; |
| } u; |
| |
| struct msm_cmd *cmd; /* current cmd */ |
| struct fd_bo *ring_bo; |
| }; |
| FD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer); |
| |
| static void finalize_current_cmd(struct fd_ringbuffer *ring); |
| static struct fd_ringbuffer * msm_ringbuffer_init( |
| struct msm_ringbuffer *msm_ring, |
| uint32_t size, enum fd_ringbuffer_flags flags); |
| |
| /* add (if needed) bo to submit and return index: */ |
| static uint32_t |
| append_bo(struct msm_submit *submit, struct fd_bo *bo) |
| { |
| struct msm_bo *msm_bo = to_msm_bo(bo); |
| uint32_t idx; |
| |
| /* NOTE: it is legal to use the same bo on different threads for |
| * different submits. But it is not legal to use the same submit |
| * from given threads. |
| */ |
| idx = READ_ONCE(msm_bo->idx); |
| |
| if (unlikely((idx >= submit->nr_submit_bos) || |
| (submit->submit_bos[idx].handle != bo->handle))) { |
| uint32_t hash = _mesa_hash_pointer(bo); |
| struct hash_entry *entry; |
| |
| entry = _mesa_hash_table_search_pre_hashed(submit->bo_table, hash, bo); |
| if (entry) { |
| /* found */ |
| idx = (uint32_t)(uintptr_t)entry->data; |
| } else { |
| idx = APPEND(submit, submit_bos); |
| idx = APPEND(submit, bos); |
| |
| submit->submit_bos[idx].flags = bo->flags & |
| (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE); |
| submit->submit_bos[idx].handle = bo->handle; |
| submit->submit_bos[idx].presumed = 0; |
| |
| submit->bos[idx] = fd_bo_ref(bo); |
| |
| _mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo, |
| (void *)(uintptr_t)idx); |
| } |
| msm_bo->idx = idx; |
| } |
| |
| return idx; |
| } |
| |
| static void |
| append_ring(struct set *set, struct fd_ringbuffer *ring) |
| { |
| uint32_t hash = _mesa_hash_pointer(ring); |
| |
| if (!_mesa_set_search_pre_hashed(set, hash, ring)) { |
| fd_ringbuffer_ref(ring); |
| _mesa_set_add_pre_hashed(set, hash, ring); |
| } |
| } |
| |
| static void |
| msm_submit_suballoc_ring_bo(struct fd_submit *submit, |
| struct msm_ringbuffer *msm_ring, uint32_t size) |
| { |
| struct msm_submit *msm_submit = to_msm_submit(submit); |
| unsigned suballoc_offset = 0; |
| struct fd_bo *suballoc_bo = NULL; |
| |
| if (msm_submit->suballoc_ring) { |
| struct msm_ringbuffer *suballoc_ring = |
| to_msm_ringbuffer(msm_submit->suballoc_ring); |
| |
| suballoc_bo = suballoc_ring->ring_bo; |
| suballoc_offset = fd_ringbuffer_size(msm_submit->suballoc_ring) + |
| suballoc_ring->offset; |
| |
| suballoc_offset = align(suballoc_offset, 0x10); |
| |
| if ((size + suballoc_offset) > suballoc_bo->size) { |
| suballoc_bo = NULL; |
| } |
| } |
| |
| if (!suballoc_bo) { |
| // TODO possibly larger size for streaming bo? |
| msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, 0x8000); |
| msm_ring->offset = 0; |
| } else { |
| msm_ring->ring_bo = fd_bo_ref(suballoc_bo); |
| msm_ring->offset = suballoc_offset; |
| } |
| |
| struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring; |
| |
| msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base); |
| |
| if (old_suballoc_ring) |
| fd_ringbuffer_del(old_suballoc_ring); |
| } |
| |
| static struct fd_ringbuffer * |
| msm_submit_new_ringbuffer(struct fd_submit *submit, uint32_t size, |
| enum fd_ringbuffer_flags flags) |
| { |
| struct msm_submit *msm_submit = to_msm_submit(submit); |
| struct msm_ringbuffer *msm_ring; |
| |
| msm_ring = slab_alloc_st(&msm_submit->ring_pool); |
| |
| msm_ring->u.submit = submit; |
| |
| /* NOTE: needs to be before _suballoc_ring_bo() since it could |
| * increment the refcnt of the current ring |
| */ |
| msm_ring->base.refcnt = 1; |
| |
| if (flags & FD_RINGBUFFER_STREAMING) { |
| msm_submit_suballoc_ring_bo(submit, msm_ring, size); |
| } else { |
| if (flags & FD_RINGBUFFER_GROWABLE) |
| size = INIT_SIZE; |
| |
| msm_ring->offset = 0; |
| msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size); |
| } |
| |
| if (!msm_ringbuffer_init(msm_ring, size, flags)) |
| return NULL; |
| |
| if (flags & FD_RINGBUFFER_PRIMARY) { |
| debug_assert(!msm_submit->primary); |
| msm_submit->primary = fd_ringbuffer_ref(&msm_ring->base); |
| } |
| |
| return &msm_ring->base; |
| } |
| |
| static struct drm_msm_gem_submit_reloc * |
| handle_stateobj_relocs(struct msm_submit *submit, struct msm_ringbuffer *ring) |
| { |
| struct msm_cmd *cmd = ring->cmd; |
| struct drm_msm_gem_submit_reloc *relocs; |
| |
| relocs = malloc(cmd->nr_relocs * sizeof(*relocs)); |
| |
| for (unsigned i = 0; i < cmd->nr_relocs; i++) { |
| unsigned idx = cmd->relocs[i].reloc_idx; |
| struct fd_bo *bo = ring->u.reloc_bos[idx]; |
| |
| relocs[i] = cmd->relocs[i]; |
| relocs[i].reloc_idx = append_bo(submit, bo); |
| } |
| |
| return relocs; |
| } |
| |
| static int |
| msm_submit_flush(struct fd_submit *submit, int in_fence_fd, |
| int *out_fence_fd, uint32_t *out_fence) |
| { |
| struct msm_submit *msm_submit = to_msm_submit(submit); |
| struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe); |
| struct drm_msm_gem_submit req = { |
| .flags = msm_pipe->pipe, |
| .queueid = msm_pipe->queue_id, |
| }; |
| int ret; |
| |
| debug_assert(msm_submit->primary); |
| |
| finalize_current_cmd(msm_submit->primary); |
| append_ring(msm_submit->ring_set, msm_submit->primary); |
| |
| unsigned nr_cmds = 0; |
| unsigned nr_objs = 0; |
| |
| set_foreach(msm_submit->ring_set, entry) { |
| struct fd_ringbuffer *ring = (void *)entry->key; |
| if (ring->flags & _FD_RINGBUFFER_OBJECT) { |
| nr_cmds += 1; |
| nr_objs += 1; |
| } else { |
| if (ring != msm_submit->primary) |
| finalize_current_cmd(ring); |
| nr_cmds += to_msm_ringbuffer(ring)->u.nr_cmds; |
| } |
| } |
| |
| void *obj_relocs[nr_objs]; |
| struct drm_msm_gem_submit_cmd cmds[nr_cmds]; |
| unsigned i = 0, o = 0; |
| |
| set_foreach(msm_submit->ring_set, entry) { |
| struct fd_ringbuffer *ring = (void *)entry->key; |
| struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); |
| |
| debug_assert(i < nr_cmds); |
| |
| // TODO handle relocs: |
| if (ring->flags & _FD_RINGBUFFER_OBJECT) { |
| |
| debug_assert(o < nr_objs); |
| |
| void *relocs = handle_stateobj_relocs(msm_submit, msm_ring); |
| obj_relocs[o++] = relocs; |
| |
| cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF; |
| cmds[i].submit_idx = |
| append_bo(msm_submit, msm_ring->ring_bo); |
| cmds[i].submit_offset = msm_ring->offset; |
| cmds[i].size = offset_bytes(ring->cur, ring->start); |
| cmds[i].pad = 0; |
| cmds[i].nr_relocs = msm_ring->cmd->nr_relocs; |
| cmds[i].relocs = VOID2U64(relocs); |
| |
| i++; |
| } else { |
| for (unsigned j = 0; j < msm_ring->u.nr_cmds; j++) { |
| if (ring->flags & FD_RINGBUFFER_PRIMARY) { |
| cmds[i].type = MSM_SUBMIT_CMD_BUF; |
| } else { |
| cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF; |
| } |
| cmds[i].submit_idx = append_bo(msm_submit, |
| msm_ring->u.cmds[j]->ring_bo); |
| cmds[i].submit_offset = msm_ring->offset; |
| cmds[i].size = msm_ring->u.cmds[j]->size; |
| cmds[i].pad = 0; |
| cmds[i].nr_relocs = msm_ring->u.cmds[j]->nr_relocs; |
| cmds[i].relocs = VOID2U64(msm_ring->u.cmds[j]->relocs); |
| |
| i++; |
| } |
| } |
| } |
| |
| if (in_fence_fd != -1) { |
| req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT; |
| req.fence_fd = in_fence_fd; |
| } |
| |
| if (out_fence_fd) { |
| req.flags |= MSM_SUBMIT_FENCE_FD_OUT; |
| } |
| |
| /* needs to be after get_cmd() as that could create bos/cmds table: */ |
| req.bos = VOID2U64(msm_submit->submit_bos), |
| req.nr_bos = msm_submit->nr_submit_bos; |
| req.cmds = VOID2U64(cmds), |
| req.nr_cmds = nr_cmds; |
| |
| DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos); |
| |
| ret = drmCommandWriteRead(submit->pipe->dev->fd, DRM_MSM_GEM_SUBMIT, |
| &req, sizeof(req)); |
| if (ret) { |
| ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno)); |
| msm_dump_submit(&req); |
| } else if (!ret) { |
| if (out_fence) |
| *out_fence = req.fence; |
| |
| if (out_fence_fd) |
| *out_fence_fd = req.fence_fd; |
| } |
| |
| for (unsigned o = 0; o < nr_objs; o++) |
| free(obj_relocs[o]); |
| |
| return ret; |
| } |
| |
| static void |
| unref_rings(struct set_entry *entry) |
| { |
| struct fd_ringbuffer *ring = (void *)entry->key; |
| fd_ringbuffer_del(ring); |
| } |
| |
| static void |
| msm_submit_destroy(struct fd_submit *submit) |
| { |
| struct msm_submit *msm_submit = to_msm_submit(submit); |
| |
| if (msm_submit->primary) |
| fd_ringbuffer_del(msm_submit->primary); |
| if (msm_submit->suballoc_ring) |
| fd_ringbuffer_del(msm_submit->suballoc_ring); |
| |
| _mesa_hash_table_destroy(msm_submit->bo_table, NULL); |
| _mesa_set_destroy(msm_submit->ring_set, unref_rings); |
| |
| // TODO it would be nice to have a way to debug_assert() if all |
| // rb's haven't been free'd back to the slab, because that is |
| // an indication that we are leaking bo's |
| slab_destroy(&msm_submit->ring_pool); |
| |
| for (unsigned i = 0; i < msm_submit->nr_bos; i++) |
| fd_bo_del(msm_submit->bos[i]); |
| |
| free(msm_submit->submit_bos); |
| free(msm_submit->bos); |
| free(msm_submit); |
| } |
| |
| static const struct fd_submit_funcs submit_funcs = { |
| .new_ringbuffer = msm_submit_new_ringbuffer, |
| .flush = msm_submit_flush, |
| .destroy = msm_submit_destroy, |
| }; |
| |
| struct fd_submit * |
| msm_submit_new(struct fd_pipe *pipe) |
| { |
| struct msm_submit *msm_submit = calloc(1, sizeof(*msm_submit)); |
| struct fd_submit *submit; |
| |
| msm_submit->bo_table = _mesa_hash_table_create(NULL, |
| _mesa_hash_pointer, _mesa_key_pointer_equal); |
| msm_submit->ring_set = _mesa_set_create(NULL, |
| _mesa_hash_pointer, _mesa_key_pointer_equal); |
| // TODO tune size: |
| slab_create(&msm_submit->ring_pool, sizeof(struct msm_ringbuffer), 16); |
| |
| submit = &msm_submit->base; |
| submit->pipe = pipe; |
| submit->funcs = &submit_funcs; |
| |
| return submit; |
| } |
| |
| |
| static void |
| finalize_current_cmd(struct fd_ringbuffer *ring) |
| { |
| struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); |
| |
| debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT)); |
| |
| if (!msm_ring->cmd) |
| return; |
| |
| debug_assert(msm_ring->cmd->ring_bo == msm_ring->ring_bo); |
| |
| unsigned idx = APPEND(&msm_ring->u, cmds); |
| |
| msm_ring->u.cmds[idx] = msm_ring->cmd; |
| msm_ring->cmd = NULL; |
| |
| msm_ring->u.cmds[idx]->size = offset_bytes(ring->cur, ring->start); |
| } |
| |
| static void |
| msm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size) |
| { |
| struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); |
| struct fd_pipe *pipe = msm_ring->u.submit->pipe; |
| |
| debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE); |
| |
| finalize_current_cmd(ring); |
| |
| fd_bo_del(msm_ring->ring_bo); |
| msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size); |
| msm_ring->cmd = cmd_new(msm_ring->ring_bo); |
| |
| ring->start = fd_bo_map(msm_ring->ring_bo); |
| ring->end = &(ring->start[size/4]); |
| ring->cur = ring->start; |
| ring->size = size; |
| } |
| |
| static void |
| msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring, |
| const struct fd_reloc *reloc) |
| { |
| struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); |
| struct fd_pipe *pipe; |
| unsigned reloc_idx; |
| |
| if (ring->flags & _FD_RINGBUFFER_OBJECT) { |
| unsigned idx = APPEND(&msm_ring->u, reloc_bos); |
| |
| msm_ring->u.reloc_bos[idx] = fd_bo_ref(reloc->bo); |
| |
| /* this gets fixed up at submit->flush() time, since this state- |
| * object rb can be used with many different submits |
| */ |
| reloc_idx = idx; |
| |
| pipe = msm_ring->u.pipe; |
| } else { |
| struct msm_submit *msm_submit = |
| to_msm_submit(msm_ring->u.submit); |
| |
| reloc_idx = append_bo(msm_submit, reloc->bo); |
| |
| pipe = msm_ring->u.submit->pipe; |
| } |
| |
| struct drm_msm_gem_submit_reloc *r; |
| unsigned idx = APPEND(msm_ring->cmd, relocs); |
| |
| r = &msm_ring->cmd->relocs[idx]; |
| |
| r->reloc_idx = reloc_idx; |
| r->reloc_offset = reloc->offset; |
| r->or = reloc->or; |
| r->shift = reloc->shift; |
| r->submit_offset = offset_bytes(ring->cur, ring->start) + |
| msm_ring->offset; |
| |
| ring->cur++; |
| |
| if (pipe->gpu_id >= 500) { |
| idx = APPEND(msm_ring->cmd, relocs); |
| r = &msm_ring->cmd->relocs[idx]; |
| |
| r->reloc_idx = reloc_idx; |
| r->reloc_offset = reloc->offset; |
| r->or = reloc->orhi; |
| r->shift = reloc->shift - 32; |
| r->submit_offset = offset_bytes(ring->cur, ring->start) + |
| msm_ring->offset; |
| |
| ring->cur++; |
| } |
| } |
| |
| static void |
| append_stateobj_rings(struct msm_submit *submit, struct fd_ringbuffer *target) |
| { |
| struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target); |
| |
| debug_assert(target->flags & _FD_RINGBUFFER_OBJECT); |
| |
| set_foreach(msm_target->u.ring_set, entry) { |
| struct fd_ringbuffer *ring = (void *)entry->key; |
| |
| append_ring(submit->ring_set, ring); |
| |
| if (ring->flags & _FD_RINGBUFFER_OBJECT) { |
| append_stateobj_rings(submit, ring); |
| } |
| } |
| } |
| |
| static uint32_t |
| msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring, |
| struct fd_ringbuffer *target, uint32_t cmd_idx) |
| { |
| struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target); |
| struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); |
| struct fd_bo *bo; |
| uint32_t size; |
| |
| if ((target->flags & FD_RINGBUFFER_GROWABLE) && |
| (cmd_idx < msm_target->u.nr_cmds)) { |
| bo = msm_target->u.cmds[cmd_idx]->ring_bo; |
| size = msm_target->u.cmds[cmd_idx]->size; |
| } else { |
| bo = msm_target->ring_bo; |
| size = offset_bytes(target->cur, target->start); |
| } |
| |
| msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){ |
| .bo = bo, |
| .offset = msm_target->offset, |
| }); |
| |
| if (!size) |
| return 0; |
| |
| if ((target->flags & _FD_RINGBUFFER_OBJECT) && |
| !(ring->flags & _FD_RINGBUFFER_OBJECT)) { |
| struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit); |
| |
| append_stateobj_rings(msm_submit, target); |
| } |
| |
| if (ring->flags & _FD_RINGBUFFER_OBJECT) { |
| append_ring(msm_ring->u.ring_set, target); |
| } else { |
| struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit); |
| append_ring(msm_submit->ring_set, target); |
| } |
| |
| return size; |
| } |
| |
| static uint32_t |
| msm_ringbuffer_cmd_count(struct fd_ringbuffer *ring) |
| { |
| if (ring->flags & FD_RINGBUFFER_GROWABLE) |
| return to_msm_ringbuffer(ring)->u.nr_cmds + 1; |
| return 1; |
| } |
| |
| static void |
| msm_ringbuffer_destroy(struct fd_ringbuffer *ring) |
| { |
| struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring); |
| |
| fd_bo_del(msm_ring->ring_bo); |
| if (msm_ring->cmd) |
| cmd_free(msm_ring->cmd); |
| |
| if (ring->flags & _FD_RINGBUFFER_OBJECT) { |
| for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) { |
| fd_bo_del(msm_ring->u.reloc_bos[i]); |
| } |
| |
| _mesa_set_destroy(msm_ring->u.ring_set, unref_rings); |
| |
| free(msm_ring->u.reloc_bos); |
| free(msm_ring); |
| } else { |
| struct fd_submit *submit = msm_ring->u.submit; |
| |
| for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) { |
| cmd_free(msm_ring->u.cmds[i]); |
| } |
| |
| free(msm_ring->u.cmds); |
| slab_free_st(&to_msm_submit(submit)->ring_pool, msm_ring); |
| } |
| } |
| |
| static const struct fd_ringbuffer_funcs ring_funcs = { |
| .grow = msm_ringbuffer_grow, |
| .emit_reloc = msm_ringbuffer_emit_reloc, |
| .emit_reloc_ring = msm_ringbuffer_emit_reloc_ring, |
| .cmd_count = msm_ringbuffer_cmd_count, |
| .destroy = msm_ringbuffer_destroy, |
| }; |
| |
| static inline struct fd_ringbuffer * |
| msm_ringbuffer_init(struct msm_ringbuffer *msm_ring, uint32_t size, |
| enum fd_ringbuffer_flags flags) |
| { |
| struct fd_ringbuffer *ring = &msm_ring->base; |
| |
| debug_assert(msm_ring->ring_bo); |
| |
| uint8_t *base = fd_bo_map(msm_ring->ring_bo); |
| ring->start = (void *)(base + msm_ring->offset); |
| ring->end = &(ring->start[size/4]); |
| ring->cur = ring->start; |
| |
| ring->size = size; |
| ring->flags = flags; |
| |
| ring->funcs = &ring_funcs; |
| |
| msm_ring->u.cmds = NULL; |
| msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0; |
| |
| msm_ring->cmd = cmd_new(msm_ring->ring_bo); |
| |
| return ring; |
| } |
| |
| struct fd_ringbuffer * |
| msm_ringbuffer_new_object(struct fd_pipe *pipe, uint32_t size) |
| { |
| struct msm_ringbuffer *msm_ring = malloc(sizeof(*msm_ring)); |
| |
| msm_ring->u.pipe = pipe; |
| msm_ring->offset = 0; |
| msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size); |
| msm_ring->base.refcnt = 1; |
| |
| msm_ring->u.reloc_bos = NULL; |
| msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0; |
| |
| msm_ring->u.ring_set = _mesa_set_create(NULL, |
| _mesa_hash_pointer, _mesa_key_pointer_equal); |
| |
| return msm_ringbuffer_init(msm_ring, size, _FD_RINGBUFFER_OBJECT); |
| } |