Path: blob/21.2-virgl/src/freedreno/drm/msm_ringbuffer.c
4564 views
/*1* Copyright (C) 2012-2018 Rob Clark <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20* SOFTWARE.21*22* Authors:23* Rob Clark <[email protected]>24*/2526#include <assert.h>27#include <inttypes.h>2829#include "util/hash_table.h"30#include "util/set.h"31#include "util/slab.h"3233#include "drm/freedreno_ringbuffer.h"34#include "msm_priv.h"3536/* The legacy implementation of submit/ringbuffer, which still does the37* traditional reloc and cmd tracking38*/3940#define INIT_SIZE 0x10004142struct msm_submit {43struct fd_submit base;4445DECLARE_ARRAY(struct drm_msm_gem_submit_bo, submit_bos);46DECLARE_ARRAY(struct fd_bo *, bos);4748/* maps fd_bo to idx in bos table: */49struct hash_table *bo_table;5051struct slab_mempool ring_pool;5253/* hash-set of associated rings: */54struct set *ring_set;5556/* Allow for sub-allocation of stateobj ring buffers (ie. sharing57* the same underlying bo)..58*59* We also rely on previous stateobj having been fully constructed60* so we can reclaim extra space at it's end.61*/62struct fd_ringbuffer *suballoc_ring;63};64FD_DEFINE_CAST(fd_submit, msm_submit);6566/* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers67* and sizes. Ie. a finalized buffer can have no more commands appended to68* it.69*/70struct msm_cmd {71struct fd_bo *ring_bo;72unsigned size;73DECLARE_ARRAY(struct drm_msm_gem_submit_reloc, relocs);74};7576static struct msm_cmd *77cmd_new(struct fd_bo *ring_bo)78{79struct msm_cmd *cmd = malloc(sizeof(*cmd));80cmd->ring_bo = fd_bo_ref(ring_bo);81cmd->size = 0;82cmd->nr_relocs = cmd->max_relocs = 0;83cmd->relocs = NULL;84return cmd;85}8687static void88cmd_free(struct msm_cmd *cmd)89{90fd_bo_del(cmd->ring_bo);91free(cmd->relocs);92free(cmd);93}9495struct msm_ringbuffer {96struct fd_ringbuffer base;9798/* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */99unsigned offset;100101union {102/* for _FD_RINGBUFFER_OBJECT case: */103struct {104struct fd_pipe *pipe;105DECLARE_ARRAY(struct fd_bo *, reloc_bos);106struct set *ring_set;107};108/* for other cases: */109struct {110struct fd_submit *submit;111DECLARE_ARRAY(struct msm_cmd *, cmds);112};113} u;114115struct msm_cmd *cmd; /* current cmd */116struct fd_bo *ring_bo;117};118FD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer);119120static void finalize_current_cmd(struct fd_ringbuffer *ring);121static struct fd_ringbuffer *122msm_ringbuffer_init(struct msm_ringbuffer *msm_ring, uint32_t size,123enum fd_ringbuffer_flags flags);124125/* add (if needed) bo to submit and return index: */126static uint32_t127append_bo(struct msm_submit *submit, struct fd_bo *bo)128{129struct msm_bo *msm_bo = to_msm_bo(bo);130uint32_t idx;131132/* NOTE: it is legal to use the same bo on different threads for133* different submits. But it is not legal to use the same submit134* from given threads.135*/136idx = READ_ONCE(msm_bo->idx);137138if (unlikely((idx >= submit->nr_submit_bos) ||139(submit->submit_bos[idx].handle != bo->handle))) {140uint32_t hash = _mesa_hash_pointer(bo);141struct hash_entry *entry;142143entry = _mesa_hash_table_search_pre_hashed(submit->bo_table, hash, bo);144if (entry) {145/* found */146idx = (uint32_t)(uintptr_t)entry->data;147} else {148idx = APPEND(149submit, submit_bos,150(struct drm_msm_gem_submit_bo){151.flags = bo->flags & (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE),152.handle = bo->handle,153.presumed = 0,154});155APPEND(submit, bos, fd_bo_ref(bo));156157_mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo,158(void *)(uintptr_t)idx);159}160msm_bo->idx = idx;161}162163return idx;164}165166static void167append_ring(struct set *set, struct fd_ringbuffer *ring)168{169uint32_t hash = _mesa_hash_pointer(ring);170171if (!_mesa_set_search_pre_hashed(set, hash, ring)) {172fd_ringbuffer_ref(ring);173_mesa_set_add_pre_hashed(set, hash, ring);174}175}176177static void178msm_submit_suballoc_ring_bo(struct fd_submit *submit,179struct msm_ringbuffer *msm_ring, uint32_t size)180{181struct msm_submit *msm_submit = to_msm_submit(submit);182unsigned suballoc_offset = 0;183struct fd_bo *suballoc_bo = NULL;184185if (msm_submit->suballoc_ring) {186struct msm_ringbuffer *suballoc_ring =187to_msm_ringbuffer(msm_submit->suballoc_ring);188189suballoc_bo = suballoc_ring->ring_bo;190suballoc_offset =191fd_ringbuffer_size(msm_submit->suballoc_ring) + suballoc_ring->offset;192193suballoc_offset = align(suballoc_offset, 0x10);194195if ((size + suballoc_offset) > suballoc_bo->size) {196suballoc_bo = NULL;197}198}199200if (!suballoc_bo) {201// TODO possibly larger size for streaming bo?202msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, 0x8000);203msm_ring->offset = 0;204} else {205msm_ring->ring_bo = fd_bo_ref(suballoc_bo);206msm_ring->offset = suballoc_offset;207}208209struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring;210211msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base);212213if (old_suballoc_ring)214fd_ringbuffer_del(old_suballoc_ring);215}216217static struct fd_ringbuffer *218msm_submit_new_ringbuffer(struct fd_submit *submit, uint32_t size,219enum fd_ringbuffer_flags flags)220{221struct msm_submit *msm_submit = to_msm_submit(submit);222struct msm_ringbuffer *msm_ring;223224msm_ring = slab_alloc_st(&msm_submit->ring_pool);225226msm_ring->u.submit = submit;227228/* NOTE: needs to be before _suballoc_ring_bo() since it could229* increment the refcnt of the current ring230*/231msm_ring->base.refcnt = 1;232233if (flags & FD_RINGBUFFER_STREAMING) {234msm_submit_suballoc_ring_bo(submit, msm_ring, size);235} else {236if (flags & FD_RINGBUFFER_GROWABLE)237size = INIT_SIZE;238239msm_ring->offset = 0;240msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size);241}242243if (!msm_ringbuffer_init(msm_ring, size, flags))244return NULL;245246return &msm_ring->base;247}248249static struct drm_msm_gem_submit_reloc *250handle_stateobj_relocs(struct msm_submit *submit, struct msm_ringbuffer *ring)251{252struct msm_cmd *cmd = ring->cmd;253struct drm_msm_gem_submit_reloc *relocs;254255relocs = malloc(cmd->nr_relocs * sizeof(*relocs));256257for (unsigned i = 0; i < cmd->nr_relocs; i++) {258unsigned idx = cmd->relocs[i].reloc_idx;259struct fd_bo *bo = ring->u.reloc_bos[idx];260261relocs[i] = cmd->relocs[i];262relocs[i].reloc_idx = append_bo(submit, bo);263}264265return relocs;266}267268static int269msm_submit_flush(struct fd_submit *submit, int in_fence_fd,270struct fd_submit_fence *out_fence)271{272struct msm_submit *msm_submit = to_msm_submit(submit);273struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);274struct drm_msm_gem_submit req = {275.flags = msm_pipe->pipe,276.queueid = msm_pipe->queue_id,277};278int ret;279280finalize_current_cmd(submit->primary);281append_ring(msm_submit->ring_set, submit->primary);282283unsigned nr_cmds = 0;284unsigned nr_objs = 0;285286set_foreach (msm_submit->ring_set, entry) {287struct fd_ringbuffer *ring = (void *)entry->key;288if (ring->flags & _FD_RINGBUFFER_OBJECT) {289nr_cmds += 1;290nr_objs += 1;291} else {292if (ring != submit->primary)293finalize_current_cmd(ring);294nr_cmds += to_msm_ringbuffer(ring)->u.nr_cmds;295}296}297298void *obj_relocs[nr_objs];299struct drm_msm_gem_submit_cmd cmds[nr_cmds];300unsigned i = 0, o = 0;301302set_foreach (msm_submit->ring_set, entry) {303struct fd_ringbuffer *ring = (void *)entry->key;304struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);305306debug_assert(i < nr_cmds);307308// TODO handle relocs:309if (ring->flags & _FD_RINGBUFFER_OBJECT) {310311debug_assert(o < nr_objs);312313void *relocs = handle_stateobj_relocs(msm_submit, msm_ring);314obj_relocs[o++] = relocs;315316cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF;317cmds[i].submit_idx = append_bo(msm_submit, msm_ring->ring_bo);318cmds[i].submit_offset = msm_ring->offset;319cmds[i].size = offset_bytes(ring->cur, ring->start);320cmds[i].pad = 0;321cmds[i].nr_relocs = msm_ring->cmd->nr_relocs;322cmds[i].relocs = VOID2U64(relocs);323324i++;325} else {326for (unsigned j = 0; j < msm_ring->u.nr_cmds; j++) {327if (ring->flags & FD_RINGBUFFER_PRIMARY) {328cmds[i].type = MSM_SUBMIT_CMD_BUF;329} else {330cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF;331}332cmds[i].submit_idx =333append_bo(msm_submit, msm_ring->u.cmds[j]->ring_bo);334cmds[i].submit_offset = msm_ring->offset;335cmds[i].size = msm_ring->u.cmds[j]->size;336cmds[i].pad = 0;337cmds[i].nr_relocs = msm_ring->u.cmds[j]->nr_relocs;338cmds[i].relocs = VOID2U64(msm_ring->u.cmds[j]->relocs);339340i++;341}342}343}344345simple_mtx_lock(&table_lock);346for (unsigned j = 0; j < msm_submit->nr_bos; j++) {347fd_bo_add_fence(msm_submit->bos[j], submit->pipe, submit->fence);348}349simple_mtx_unlock(&table_lock);350351if (in_fence_fd != -1) {352req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;353req.fence_fd = in_fence_fd;354}355356if (out_fence && out_fence->use_fence_fd) {357req.flags |= MSM_SUBMIT_FENCE_FD_OUT;358}359360/* needs to be after get_cmd() as that could create bos/cmds table: */361req.bos = VOID2U64(msm_submit->submit_bos),362req.nr_bos = msm_submit->nr_submit_bos;363req.cmds = VOID2U64(cmds), req.nr_cmds = nr_cmds;364365DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);366367ret = drmCommandWriteRead(submit->pipe->dev->fd, DRM_MSM_GEM_SUBMIT, &req,368sizeof(req));369if (ret) {370ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));371msm_dump_submit(&req);372} else if (!ret && out_fence) {373out_fence->fence.kfence = req.fence;374out_fence->fence.ufence = submit->fence;375out_fence->fence_fd = req.fence_fd;376}377378for (unsigned o = 0; o < nr_objs; o++)379free(obj_relocs[o]);380381return ret;382}383384static void385unref_rings(struct set_entry *entry)386{387struct fd_ringbuffer *ring = (void *)entry->key;388fd_ringbuffer_del(ring);389}390391static void392msm_submit_destroy(struct fd_submit *submit)393{394struct msm_submit *msm_submit = to_msm_submit(submit);395396if (msm_submit->suballoc_ring)397fd_ringbuffer_del(msm_submit->suballoc_ring);398399_mesa_hash_table_destroy(msm_submit->bo_table, NULL);400_mesa_set_destroy(msm_submit->ring_set, unref_rings);401402// TODO it would be nice to have a way to debug_assert() if all403// rb's haven't been free'd back to the slab, because that is404// an indication that we are leaking bo's405slab_destroy(&msm_submit->ring_pool);406407for (unsigned i = 0; i < msm_submit->nr_bos; i++)408fd_bo_del(msm_submit->bos[i]);409410free(msm_submit->submit_bos);411free(msm_submit->bos);412free(msm_submit);413}414415static const struct fd_submit_funcs submit_funcs = {416.new_ringbuffer = msm_submit_new_ringbuffer,417.flush = msm_submit_flush,418.destroy = msm_submit_destroy,419};420421struct fd_submit *422msm_submit_new(struct fd_pipe *pipe)423{424struct msm_submit *msm_submit = calloc(1, sizeof(*msm_submit));425struct fd_submit *submit;426427msm_submit->bo_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,428_mesa_key_pointer_equal);429msm_submit->ring_set =430_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);431// TODO tune size:432slab_create(&msm_submit->ring_pool, sizeof(struct msm_ringbuffer), 16);433434submit = &msm_submit->base;435submit->funcs = &submit_funcs;436437return submit;438}439440static void441finalize_current_cmd(struct fd_ringbuffer *ring)442{443struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);444445debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));446447if (!msm_ring->cmd)448return;449450debug_assert(msm_ring->cmd->ring_bo == msm_ring->ring_bo);451452msm_ring->cmd->size = offset_bytes(ring->cur, ring->start);453APPEND(&msm_ring->u, cmds, msm_ring->cmd);454msm_ring->cmd = NULL;455}456457static void458msm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size)459{460struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);461struct fd_pipe *pipe = msm_ring->u.submit->pipe;462463debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE);464465finalize_current_cmd(ring);466467fd_bo_del(msm_ring->ring_bo);468msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);469msm_ring->cmd = cmd_new(msm_ring->ring_bo);470471ring->start = fd_bo_map(msm_ring->ring_bo);472ring->end = &(ring->start[size / 4]);473ring->cur = ring->start;474ring->size = size;475}476477static void478msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,479const struct fd_reloc *reloc)480{481struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);482struct fd_pipe *pipe;483unsigned reloc_idx;484485if (ring->flags & _FD_RINGBUFFER_OBJECT) {486unsigned idx = APPEND(&msm_ring->u, reloc_bos, fd_bo_ref(reloc->bo));487488/* this gets fixed up at submit->flush() time, since this state-489* object rb can be used with many different submits490*/491reloc_idx = idx;492493pipe = msm_ring->u.pipe;494} else {495struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);496497reloc_idx = append_bo(msm_submit, reloc->bo);498499pipe = msm_ring->u.submit->pipe;500}501502APPEND(msm_ring->cmd, relocs,503(struct drm_msm_gem_submit_reloc){504.reloc_idx = reloc_idx,505.reloc_offset = reloc->offset,506.or = reloc->orlo,507.shift = reloc->shift,508.submit_offset =509offset_bytes(ring->cur, ring->start) + msm_ring->offset,510});511512ring->cur++;513514if (pipe->gpu_id >= 500) {515APPEND(msm_ring->cmd, relocs,516(struct drm_msm_gem_submit_reloc){517.reloc_idx = reloc_idx,518.reloc_offset = reloc->offset,519.or = reloc->orhi,520.shift = reloc->shift - 32,521.submit_offset =522offset_bytes(ring->cur, ring->start) + msm_ring->offset,523});524525ring->cur++;526}527}528529static void530append_stateobj_rings(struct msm_submit *submit, struct fd_ringbuffer *target)531{532struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);533534debug_assert(target->flags & _FD_RINGBUFFER_OBJECT);535536set_foreach (msm_target->u.ring_set, entry) {537struct fd_ringbuffer *ring = (void *)entry->key;538539append_ring(submit->ring_set, ring);540541if (ring->flags & _FD_RINGBUFFER_OBJECT) {542append_stateobj_rings(submit, ring);543}544}545}546547static uint32_t548msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,549struct fd_ringbuffer *target, uint32_t cmd_idx)550{551struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);552struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);553struct fd_bo *bo;554uint32_t size;555556if ((target->flags & FD_RINGBUFFER_GROWABLE) &&557(cmd_idx < msm_target->u.nr_cmds)) {558bo = msm_target->u.cmds[cmd_idx]->ring_bo;559size = msm_target->u.cmds[cmd_idx]->size;560} else {561bo = msm_target->ring_bo;562size = offset_bytes(target->cur, target->start);563}564565msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){566.bo = bo,567.iova = bo->iova + msm_target->offset,568.offset = msm_target->offset,569});570571if (!size)572return 0;573574if ((target->flags & _FD_RINGBUFFER_OBJECT) &&575!(ring->flags & _FD_RINGBUFFER_OBJECT)) {576struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);577578append_stateobj_rings(msm_submit, target);579}580581if (ring->flags & _FD_RINGBUFFER_OBJECT) {582append_ring(msm_ring->u.ring_set, target);583} else {584struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);585append_ring(msm_submit->ring_set, target);586}587588return size;589}590591static uint32_t592msm_ringbuffer_cmd_count(struct fd_ringbuffer *ring)593{594if (ring->flags & FD_RINGBUFFER_GROWABLE)595return to_msm_ringbuffer(ring)->u.nr_cmds + 1;596return 1;597}598599static bool600msm_ringbuffer_check_size(struct fd_ringbuffer *ring)601{602assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));603struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);604struct fd_submit *submit = msm_ring->u.submit;605struct fd_pipe *pipe = submit->pipe;606607if ((fd_device_version(pipe->dev) < FD_VERSION_UNLIMITED_CMDS) &&608((ring->cur - ring->start) > (ring->size / 4 - 0x1000))) {609return false;610}611612if (to_msm_submit(submit)->nr_bos > MAX_ARRAY_SIZE/2) {613return false;614}615616return true;617}618619static void620msm_ringbuffer_destroy(struct fd_ringbuffer *ring)621{622struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);623624fd_bo_del(msm_ring->ring_bo);625if (msm_ring->cmd)626cmd_free(msm_ring->cmd);627628if (ring->flags & _FD_RINGBUFFER_OBJECT) {629for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) {630fd_bo_del(msm_ring->u.reloc_bos[i]);631}632633_mesa_set_destroy(msm_ring->u.ring_set, unref_rings);634635free(msm_ring->u.reloc_bos);636free(msm_ring);637} else {638struct fd_submit *submit = msm_ring->u.submit;639640for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) {641cmd_free(msm_ring->u.cmds[i]);642}643644free(msm_ring->u.cmds);645slab_free_st(&to_msm_submit(submit)->ring_pool, msm_ring);646}647}648649static const struct fd_ringbuffer_funcs ring_funcs = {650.grow = msm_ringbuffer_grow,651.emit_reloc = msm_ringbuffer_emit_reloc,652.emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,653.cmd_count = msm_ringbuffer_cmd_count,654.check_size = msm_ringbuffer_check_size,655.destroy = msm_ringbuffer_destroy,656};657658static inline struct fd_ringbuffer *659msm_ringbuffer_init(struct msm_ringbuffer *msm_ring, uint32_t size,660enum fd_ringbuffer_flags flags)661{662struct fd_ringbuffer *ring = &msm_ring->base;663664debug_assert(msm_ring->ring_bo);665666uint8_t *base = fd_bo_map(msm_ring->ring_bo);667ring->start = (void *)(base + msm_ring->offset);668ring->end = &(ring->start[size / 4]);669ring->cur = ring->start;670671ring->size = size;672ring->flags = flags;673674ring->funcs = &ring_funcs;675676msm_ring->u.cmds = NULL;677msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0;678679msm_ring->cmd = cmd_new(msm_ring->ring_bo);680681return ring;682}683684struct fd_ringbuffer *685msm_ringbuffer_new_object(struct fd_pipe *pipe, uint32_t size)686{687struct msm_ringbuffer *msm_ring = malloc(sizeof(*msm_ring));688689msm_ring->u.pipe = pipe;690msm_ring->offset = 0;691msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);692msm_ring->base.refcnt = 1;693694msm_ring->u.reloc_bos = NULL;695msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0;696697msm_ring->u.ring_set =698_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);699700return msm_ringbuffer_init(msm_ring, size, _FD_RINGBUFFER_OBJECT);701}702703704