Path: blob/21.2-virgl/src/freedreno/drm/msm_ringbuffer_sp.c
4564 views
/*1* Copyright (C) 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>28#include <pthread.h>2930#include "util/hash_table.h"31#include "util/os_file.h"32#include "util/slab.h"3334#include "drm/freedreno_ringbuffer.h"35#include "msm_priv.h"3637/* A "softpin" implementation of submit/ringbuffer, which lowers CPU overhead38* by avoiding the additional tracking necessary to build cmds/relocs tables39* (but still builds a bos table)40*/4142#define INIT_SIZE 0x10004344#define SUBALLOC_SIZE (32 * 1024)4546/* In the pipe->flush() path, we don't have a util_queue_fence we can wait on,47* instead use a condition-variable. Note that pipe->flush() is not expected48* to be a common/hot path.49*/50static pthread_cond_t flush_cnd = PTHREAD_COND_INITIALIZER;51static pthread_mutex_t flush_mtx = PTHREAD_MUTEX_INITIALIZER;525354struct msm_submit_sp {55struct fd_submit base;5657DECLARE_ARRAY(struct fd_bo *, bos);5859/* maps fd_bo to idx in bos table: */60struct hash_table *bo_table;6162struct slab_child_pool ring_pool;6364/* Allow for sub-allocation of stateobj ring buffers (ie. sharing65* the same underlying bo)..66*67* We also rely on previous stateobj having been fully constructed68* so we can reclaim extra space at it's end.69*/70struct fd_ringbuffer *suballoc_ring;7172/* Flush args, potentially attached to the last submit in the list73* of submits to merge:74*/75int in_fence_fd;76struct fd_submit_fence *out_fence;7778/* State for enqueued submits:79*/80struct list_head submit_list; /* includes this submit as last element */8182/* Used in case out_fence==NULL: */83struct util_queue_fence fence;84};85FD_DEFINE_CAST(fd_submit, msm_submit_sp);8687/* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers88* and sizes. Ie. a finalized buffer can have no more commands appended to89* it.90*/91struct msm_cmd_sp {92struct fd_bo *ring_bo;93unsigned size;94};9596struct msm_ringbuffer_sp {97struct fd_ringbuffer base;9899/* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */100unsigned offset;101102union {103/* for _FD_RINGBUFFER_OBJECT case, the array of BOs referenced from104* this one105*/106struct {107struct fd_pipe *pipe;108DECLARE_ARRAY(struct fd_bo *, reloc_bos);109};110/* for other cases: */111struct {112struct fd_submit *submit;113DECLARE_ARRAY(struct msm_cmd_sp, cmds);114};115} u;116117struct fd_bo *ring_bo;118};119FD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer_sp);120121static void finalize_current_cmd(struct fd_ringbuffer *ring);122static struct fd_ringbuffer *123msm_ringbuffer_sp_init(struct msm_ringbuffer_sp *msm_ring, uint32_t size,124enum fd_ringbuffer_flags flags);125126/* add (if needed) bo to submit and return index: */127static uint32_t128msm_submit_append_bo(struct msm_submit_sp *submit, struct fd_bo *bo)129{130struct msm_bo *msm_bo = to_msm_bo(bo);131uint32_t idx;132133/* NOTE: it is legal to use the same bo on different threads for134* different submits. But it is not legal to use the same submit135* from different threads.136*/137idx = READ_ONCE(msm_bo->idx);138139if (unlikely((idx >= submit->nr_bos) || (submit->bos[idx] != bo))) {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(submit, bos, fd_bo_ref(bo));149150_mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo,151(void *)(uintptr_t)idx);152}153msm_bo->idx = idx;154}155156return idx;157}158159static void160msm_submit_suballoc_ring_bo(struct fd_submit *submit,161struct msm_ringbuffer_sp *msm_ring, uint32_t size)162{163struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);164unsigned suballoc_offset = 0;165struct fd_bo *suballoc_bo = NULL;166167if (msm_submit->suballoc_ring) {168struct msm_ringbuffer_sp *suballoc_ring =169to_msm_ringbuffer_sp(msm_submit->suballoc_ring);170171suballoc_bo = suballoc_ring->ring_bo;172suballoc_offset =173fd_ringbuffer_size(msm_submit->suballoc_ring) + suballoc_ring->offset;174175suballoc_offset = align(suballoc_offset, 0x10);176177if ((size + suballoc_offset) > suballoc_bo->size) {178suballoc_bo = NULL;179}180}181182if (!suballoc_bo) {183// TODO possibly larger size for streaming bo?184msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, SUBALLOC_SIZE);185msm_ring->offset = 0;186} else {187msm_ring->ring_bo = fd_bo_ref(suballoc_bo);188msm_ring->offset = suballoc_offset;189}190191struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring;192193msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base);194195if (old_suballoc_ring)196fd_ringbuffer_del(old_suballoc_ring);197}198199static struct fd_ringbuffer *200msm_submit_sp_new_ringbuffer(struct fd_submit *submit, uint32_t size,201enum fd_ringbuffer_flags flags)202{203struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);204struct msm_ringbuffer_sp *msm_ring;205206msm_ring = slab_alloc(&msm_submit->ring_pool);207208msm_ring->u.submit = submit;209210/* NOTE: needs to be before _suballoc_ring_bo() since it could211* increment the refcnt of the current ring212*/213msm_ring->base.refcnt = 1;214215if (flags & FD_RINGBUFFER_STREAMING) {216msm_submit_suballoc_ring_bo(submit, msm_ring, size);217} else {218if (flags & FD_RINGBUFFER_GROWABLE)219size = INIT_SIZE;220221msm_ring->offset = 0;222msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size);223}224225if (!msm_ringbuffer_sp_init(msm_ring, size, flags))226return NULL;227228return &msm_ring->base;229}230231/**232* Prepare submit for flush, always done synchronously.233*234* 1) Finalize primary ringbuffer, at this point no more cmdstream may235* be written into it, since from the PoV of the upper level driver236* the submit is flushed, even if deferred237* 2) Add cmdstream bos to bos table238* 3) Update bo fences239*/240static bool241msm_submit_sp_flush_prep(struct fd_submit *submit, int in_fence_fd,242struct fd_submit_fence *out_fence)243{244struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);245bool has_shared = false;246247finalize_current_cmd(submit->primary);248249struct msm_ringbuffer_sp *primary =250to_msm_ringbuffer_sp(submit->primary);251252for (unsigned i = 0; i < primary->u.nr_cmds; i++)253msm_submit_append_bo(msm_submit, primary->u.cmds[i].ring_bo);254255simple_mtx_lock(&table_lock);256for (unsigned i = 0; i < msm_submit->nr_bos; i++) {257fd_bo_add_fence(msm_submit->bos[i], submit->pipe, submit->fence);258has_shared |= msm_submit->bos[i]->shared;259}260simple_mtx_unlock(&table_lock);261262msm_submit->out_fence = out_fence;263msm_submit->in_fence_fd = (in_fence_fd == -1) ?264-1 : os_dupfd_cloexec(in_fence_fd);265266return has_shared;267}268269static int270flush_submit_list(struct list_head *submit_list)271{272struct msm_submit_sp *msm_submit = to_msm_submit_sp(last_submit(submit_list));273struct msm_pipe *msm_pipe = to_msm_pipe(msm_submit->base.pipe);274struct drm_msm_gem_submit req = {275.flags = msm_pipe->pipe,276.queueid = msm_pipe->queue_id,277};278int ret;279280unsigned nr_cmds = 0;281282/* Determine the number of extra cmds's from deferred submits that283* we will be merging in:284*/285foreach_submit (submit, submit_list) {286assert(submit->pipe == &msm_pipe->base);287nr_cmds += to_msm_ringbuffer_sp(submit->primary)->u.nr_cmds;288}289290struct drm_msm_gem_submit_cmd cmds[nr_cmds];291292unsigned cmd_idx = 0;293294/* Build up the table of cmds, and for all but the last submit in the295* list, merge their bo tables into the last submit.296*/297foreach_submit_safe (submit, submit_list) {298struct msm_ringbuffer_sp *deferred_primary =299to_msm_ringbuffer_sp(submit->primary);300301for (unsigned i = 0; i < deferred_primary->u.nr_cmds; i++) {302cmds[cmd_idx].type = MSM_SUBMIT_CMD_BUF;303cmds[cmd_idx].submit_idx =304msm_submit_append_bo(msm_submit, deferred_primary->u.cmds[i].ring_bo);305cmds[cmd_idx].submit_offset = deferred_primary->offset;306cmds[cmd_idx].size = deferred_primary->u.cmds[i].size;307cmds[cmd_idx].pad = 0;308cmds[cmd_idx].nr_relocs = 0;309310cmd_idx++;311}312313/* We are merging all the submits in the list into the last submit,314* so the remainder of the loop body doesn't apply to the last submit315*/316if (submit == last_submit(submit_list)) {317DEBUG_MSG("merged %u submits", cmd_idx);318break;319}320321struct msm_submit_sp *msm_deferred_submit = to_msm_submit_sp(submit);322for (unsigned i = 0; i < msm_deferred_submit->nr_bos; i++) {323/* Note: if bo is used in both the current submit and the deferred324* submit being merged, we expect to hit the fast-path as we add it325* to the current submit:326*/327msm_submit_append_bo(msm_submit, msm_deferred_submit->bos[i]);328}329330/* Now that the cmds/bos have been transfered over to the current submit,331* we can remove the deferred submit from the list and drop it's reference332*/333list_del(&submit->node);334fd_submit_del(submit);335}336337if (msm_submit->in_fence_fd != -1) {338req.flags |= MSM_SUBMIT_FENCE_FD_IN;339req.fence_fd = msm_submit->in_fence_fd;340msm_pipe->no_implicit_sync = true;341}342343if (msm_pipe->no_implicit_sync) {344req.flags |= MSM_SUBMIT_NO_IMPLICIT;345}346347if (msm_submit->out_fence && msm_submit->out_fence->use_fence_fd) {348req.flags |= MSM_SUBMIT_FENCE_FD_OUT;349}350351/* Needs to be after get_cmd() as that could create bos/cmds table:352*353* NOTE allocate on-stack in the common case, but with an upper-354* bound to limit on-stack allocation to 4k:355*/356const unsigned bo_limit = sizeof(struct drm_msm_gem_submit_bo) / 4096;357bool bos_on_stack = msm_submit->nr_bos < bo_limit;358struct drm_msm_gem_submit_bo359_submit_bos[bos_on_stack ? msm_submit->nr_bos : 0];360struct drm_msm_gem_submit_bo *submit_bos;361if (bos_on_stack) {362submit_bos = _submit_bos;363} else {364submit_bos = malloc(msm_submit->nr_bos * sizeof(submit_bos[0]));365}366367for (unsigned i = 0; i < msm_submit->nr_bos; i++) {368submit_bos[i].flags = msm_submit->bos[i]->flags;369submit_bos[i].handle = msm_submit->bos[i]->handle;370submit_bos[i].presumed = 0;371}372373req.bos = VOID2U64(submit_bos);374req.nr_bos = msm_submit->nr_bos;375req.cmds = VOID2U64(cmds);376req.nr_cmds = nr_cmds;377378DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);379380ret = drmCommandWriteRead(msm_pipe->base.dev->fd, DRM_MSM_GEM_SUBMIT, &req,381sizeof(req));382if (ret) {383ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));384msm_dump_submit(&req);385} else if (!ret && msm_submit->out_fence) {386msm_submit->out_fence->fence.kfence = req.fence;387msm_submit->out_fence->fence.ufence = msm_submit->base.fence;388msm_submit->out_fence->fence_fd = req.fence_fd;389}390391if (!bos_on_stack)392free(submit_bos);393394pthread_mutex_lock(&flush_mtx);395assert(fd_fence_before(msm_pipe->last_submit_fence, msm_submit->base.fence));396msm_pipe->last_submit_fence = msm_submit->base.fence;397pthread_cond_broadcast(&flush_cnd);398pthread_mutex_unlock(&flush_mtx);399400if (msm_submit->in_fence_fd != -1)401close(msm_submit->in_fence_fd);402403return ret;404}405406static void407msm_submit_sp_flush_execute(void *job, void *gdata, int thread_index)408{409struct fd_submit *submit = job;410struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);411412flush_submit_list(&msm_submit->submit_list);413414DEBUG_MSG("finish: %u", submit->fence);415}416417static void418msm_submit_sp_flush_cleanup(void *job, void *gdata, int thread_index)419{420struct fd_submit *submit = job;421fd_submit_del(submit);422}423424static int425enqueue_submit_list(struct list_head *submit_list)426{427struct fd_submit *submit = last_submit(submit_list);428struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);429struct msm_device *msm_dev = to_msm_device(submit->pipe->dev);430431list_replace(submit_list, &msm_submit->submit_list);432list_inithead(submit_list);433434struct util_queue_fence *fence;435if (msm_submit->out_fence) {436fence = &msm_submit->out_fence->ready;437} else {438util_queue_fence_init(&msm_submit->fence);439fence = &msm_submit->fence;440}441442DEBUG_MSG("enqueue: %u", submit->fence);443444util_queue_add_job(&msm_dev->submit_queue,445submit, fence,446msm_submit_sp_flush_execute,447msm_submit_sp_flush_cleanup,4480);449450return 0;451}452453static bool454should_defer(struct fd_submit *submit)455{456struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);457458/* if too many bo's, it may not be worth the CPU cost of submit merging: */459if (msm_submit->nr_bos > 30)460return false;461462/* On the kernel side, with 32K ringbuffer, we have an upper limit of 2k463* cmds before we exceed the size of the ringbuffer, which results in464* deadlock writing into the RB (ie. kernel doesn't finish writing into465* the RB so it doesn't kick the GPU to start consuming from the RB)466*/467if (submit->pipe->dev->deferred_cmds > 128)468return false;469470return true;471}472473static int474msm_submit_sp_flush(struct fd_submit *submit, int in_fence_fd,475struct fd_submit_fence *out_fence)476{477struct fd_device *dev = submit->pipe->dev;478struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);479480/* Acquire lock before flush_prep() because it is possible to race between481* this and pipe->flush():482*/483simple_mtx_lock(&dev->submit_lock);484485/* If there are deferred submits from another fd_pipe, flush them now,486* since we can't merge submits from different submitqueue's (ie. they487* could have different priority, etc)488*/489if (!list_is_empty(&dev->deferred_submits) &&490(last_submit(&dev->deferred_submits)->pipe != submit->pipe)) {491struct list_head submit_list;492493list_replace(&dev->deferred_submits, &submit_list);494list_inithead(&dev->deferred_submits);495dev->deferred_cmds = 0;496497enqueue_submit_list(&submit_list);498}499500list_addtail(&fd_submit_ref(submit)->node, &dev->deferred_submits);501502bool has_shared = msm_submit_sp_flush_prep(submit, in_fence_fd, out_fence);503504assert(fd_fence_before(msm_pipe->last_enqueue_fence, submit->fence));505msm_pipe->last_enqueue_fence = submit->fence;506507/* If we don't need an out-fence, we can defer the submit.508*509* TODO we could defer submits with in-fence as well.. if we took our own510* reference to the fd, and merged all the in-fence-fd's when we flush the511* deferred submits512*/513if ((in_fence_fd == -1) && !out_fence && !has_shared && should_defer(submit)) {514DEBUG_MSG("defer: %u", submit->fence);515dev->deferred_cmds += fd_ringbuffer_cmd_count(submit->primary);516assert(dev->deferred_cmds == fd_dev_count_deferred_cmds(dev));517simple_mtx_unlock(&dev->submit_lock);518519return 0;520}521522struct list_head submit_list;523524list_replace(&dev->deferred_submits, &submit_list);525list_inithead(&dev->deferred_submits);526dev->deferred_cmds = 0;527528simple_mtx_unlock(&dev->submit_lock);529530return enqueue_submit_list(&submit_list);531}532533void534msm_pipe_sp_flush(struct fd_pipe *pipe, uint32_t fence)535{536struct msm_pipe *msm_pipe = to_msm_pipe(pipe);537struct fd_device *dev = pipe->dev;538struct list_head submit_list;539540DEBUG_MSG("flush: %u", fence);541542list_inithead(&submit_list);543544simple_mtx_lock(&dev->submit_lock);545546assert(!fd_fence_after(fence, msm_pipe->last_enqueue_fence));547548foreach_submit_safe (deferred_submit, &dev->deferred_submits) {549/* We should never have submits from multiple pipes in the deferred550* list. If we did, we couldn't compare their fence to our fence,551* since each fd_pipe is an independent timeline.552*/553if (deferred_submit->pipe != pipe)554break;555556if (fd_fence_after(deferred_submit->fence, fence))557break;558559list_del(&deferred_submit->node);560list_addtail(&deferred_submit->node, &submit_list);561dev->deferred_cmds -= fd_ringbuffer_cmd_count(deferred_submit->primary);562}563564assert(dev->deferred_cmds == fd_dev_count_deferred_cmds(dev));565566simple_mtx_unlock(&dev->submit_lock);567568if (list_is_empty(&submit_list))569goto flush_sync;570571enqueue_submit_list(&submit_list);572573flush_sync:574/* Once we are sure that we've enqueued at least up to the requested575* submit, we need to be sure that submitq has caught up and flushed576* them to the kernel577*/578pthread_mutex_lock(&flush_mtx);579while (fd_fence_before(msm_pipe->last_submit_fence, fence)) {580pthread_cond_wait(&flush_cnd, &flush_mtx);581}582pthread_mutex_unlock(&flush_mtx);583}584585static void586msm_submit_sp_destroy(struct fd_submit *submit)587{588struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);589590if (msm_submit->suballoc_ring)591fd_ringbuffer_del(msm_submit->suballoc_ring);592593_mesa_hash_table_destroy(msm_submit->bo_table, NULL);594595// TODO it would be nice to have a way to debug_assert() if all596// rb's haven't been free'd back to the slab, because that is597// an indication that we are leaking bo's598slab_destroy_child(&msm_submit->ring_pool);599600for (unsigned i = 0; i < msm_submit->nr_bos; i++)601fd_bo_del(msm_submit->bos[i]);602603free(msm_submit->bos);604free(msm_submit);605}606607static const struct fd_submit_funcs submit_funcs = {608.new_ringbuffer = msm_submit_sp_new_ringbuffer,609.flush = msm_submit_sp_flush,610.destroy = msm_submit_sp_destroy,611};612613struct fd_submit *614msm_submit_sp_new(struct fd_pipe *pipe)615{616struct msm_submit_sp *msm_submit = calloc(1, sizeof(*msm_submit));617struct fd_submit *submit;618619msm_submit->bo_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,620_mesa_key_pointer_equal);621622slab_create_child(&msm_submit->ring_pool, &to_msm_pipe(pipe)->ring_pool);623624submit = &msm_submit->base;625submit->funcs = &submit_funcs;626627return submit;628}629630void631msm_pipe_sp_ringpool_init(struct msm_pipe *msm_pipe)632{633// TODO tune size:634slab_create_parent(&msm_pipe->ring_pool, sizeof(struct msm_ringbuffer_sp),63516);636}637638void639msm_pipe_sp_ringpool_fini(struct msm_pipe *msm_pipe)640{641if (msm_pipe->ring_pool.num_elements)642slab_destroy_parent(&msm_pipe->ring_pool);643}644645static void646finalize_current_cmd(struct fd_ringbuffer *ring)647{648debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));649650struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);651APPEND(&msm_ring->u, cmds,652(struct msm_cmd_sp){653.ring_bo = fd_bo_ref(msm_ring->ring_bo),654.size = offset_bytes(ring->cur, ring->start),655});656}657658static void659msm_ringbuffer_sp_grow(struct fd_ringbuffer *ring, uint32_t size)660{661struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);662struct fd_pipe *pipe = msm_ring->u.submit->pipe;663664debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE);665666finalize_current_cmd(ring);667668fd_bo_del(msm_ring->ring_bo);669msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);670671ring->start = fd_bo_map(msm_ring->ring_bo);672ring->end = &(ring->start[size / 4]);673ring->cur = ring->start;674ring->size = size;675}676677static inline bool678msm_ringbuffer_references_bo(struct fd_ringbuffer *ring, struct fd_bo *bo)679{680struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);681682for (int i = 0; i < msm_ring->u.nr_reloc_bos; i++) {683if (msm_ring->u.reloc_bos[i] == bo)684return true;685}686return false;687}688689#define PTRSZ 64690#include "msm_ringbuffer_sp.h"691#undef PTRSZ692#define PTRSZ 32693#include "msm_ringbuffer_sp.h"694#undef PTRSZ695696static uint32_t697msm_ringbuffer_sp_cmd_count(struct fd_ringbuffer *ring)698{699if (ring->flags & FD_RINGBUFFER_GROWABLE)700return to_msm_ringbuffer_sp(ring)->u.nr_cmds + 1;701return 1;702}703704static bool705msm_ringbuffer_sp_check_size(struct fd_ringbuffer *ring)706{707assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));708struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);709struct fd_submit *submit = msm_ring->u.submit;710711if (to_msm_submit_sp(submit)->nr_bos > MAX_ARRAY_SIZE/2) {712return false;713}714715return true;716}717718static void719msm_ringbuffer_sp_destroy(struct fd_ringbuffer *ring)720{721struct msm_ringbuffer_sp *msm_ring = to_msm_ringbuffer_sp(ring);722723fd_bo_del(msm_ring->ring_bo);724725if (ring->flags & _FD_RINGBUFFER_OBJECT) {726for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) {727fd_bo_del(msm_ring->u.reloc_bos[i]);728}729free(msm_ring->u.reloc_bos);730731free(msm_ring);732} else {733struct fd_submit *submit = msm_ring->u.submit;734735for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) {736fd_bo_del(msm_ring->u.cmds[i].ring_bo);737}738free(msm_ring->u.cmds);739740slab_free(&to_msm_submit_sp(submit)->ring_pool, msm_ring);741}742}743744static const struct fd_ringbuffer_funcs ring_funcs_nonobj_32 = {745.grow = msm_ringbuffer_sp_grow,746.emit_reloc = msm_ringbuffer_sp_emit_reloc_nonobj_32,747.emit_reloc_ring = msm_ringbuffer_sp_emit_reloc_ring_32,748.cmd_count = msm_ringbuffer_sp_cmd_count,749.check_size = msm_ringbuffer_sp_check_size,750.destroy = msm_ringbuffer_sp_destroy,751};752753static const struct fd_ringbuffer_funcs ring_funcs_obj_32 = {754.grow = msm_ringbuffer_sp_grow,755.emit_reloc = msm_ringbuffer_sp_emit_reloc_obj_32,756.emit_reloc_ring = msm_ringbuffer_sp_emit_reloc_ring_32,757.cmd_count = msm_ringbuffer_sp_cmd_count,758.destroy = msm_ringbuffer_sp_destroy,759};760761static const struct fd_ringbuffer_funcs ring_funcs_nonobj_64 = {762.grow = msm_ringbuffer_sp_grow,763.emit_reloc = msm_ringbuffer_sp_emit_reloc_nonobj_64,764.emit_reloc_ring = msm_ringbuffer_sp_emit_reloc_ring_64,765.cmd_count = msm_ringbuffer_sp_cmd_count,766.check_size = msm_ringbuffer_sp_check_size,767.destroy = msm_ringbuffer_sp_destroy,768};769770static const struct fd_ringbuffer_funcs ring_funcs_obj_64 = {771.grow = msm_ringbuffer_sp_grow,772.emit_reloc = msm_ringbuffer_sp_emit_reloc_obj_64,773.emit_reloc_ring = msm_ringbuffer_sp_emit_reloc_ring_64,774.cmd_count = msm_ringbuffer_sp_cmd_count,775.destroy = msm_ringbuffer_sp_destroy,776};777778static inline struct fd_ringbuffer *779msm_ringbuffer_sp_init(struct msm_ringbuffer_sp *msm_ring, uint32_t size,780enum fd_ringbuffer_flags flags)781{782struct fd_ringbuffer *ring = &msm_ring->base;783784/* We don't do any translation from internal FD_RELOC flags to MSM flags. */785STATIC_ASSERT(FD_RELOC_READ == MSM_SUBMIT_BO_READ);786STATIC_ASSERT(FD_RELOC_WRITE == MSM_SUBMIT_BO_WRITE);787STATIC_ASSERT(FD_RELOC_DUMP == MSM_SUBMIT_BO_DUMP);788789debug_assert(msm_ring->ring_bo);790791uint8_t *base = fd_bo_map(msm_ring->ring_bo);792ring->start = (void *)(base + msm_ring->offset);793ring->end = &(ring->start[size / 4]);794ring->cur = ring->start;795796ring->size = size;797ring->flags = flags;798799if (flags & _FD_RINGBUFFER_OBJECT) {800if (msm_ring->u.pipe->gpu_id >= 500) {801ring->funcs = &ring_funcs_obj_64;802} else {803ring->funcs = &ring_funcs_obj_32;804}805} else {806if (msm_ring->u.submit->pipe->gpu_id >= 500) {807ring->funcs = &ring_funcs_nonobj_64;808} else {809ring->funcs = &ring_funcs_nonobj_32;810}811}812813// TODO initializing these could probably be conditional on flags814// since unneed for FD_RINGBUFFER_STAGING case..815msm_ring->u.cmds = NULL;816msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0;817818msm_ring->u.reloc_bos = NULL;819msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0;820821return ring;822}823824struct fd_ringbuffer *825msm_ringbuffer_sp_new_object(struct fd_pipe *pipe, uint32_t size)826{827struct msm_pipe *msm_pipe = to_msm_pipe(pipe);828struct msm_ringbuffer_sp *msm_ring = malloc(sizeof(*msm_ring));829830/* Lock access to the msm_pipe->suballoc_* since ringbuffer object allocation831* can happen both on the frontend (most CSOs) and the driver thread (a6xx832* cached tex state, for example)833*/834static simple_mtx_t suballoc_lock = _SIMPLE_MTX_INITIALIZER_NP;835simple_mtx_lock(&suballoc_lock);836837/* Maximum known alignment requirement is a6xx's TEX_CONST at 16 dwords */838msm_ring->offset = align(msm_pipe->suballoc_offset, 64);839if (!msm_pipe->suballoc_bo ||840msm_ring->offset + size > fd_bo_size(msm_pipe->suballoc_bo)) {841if (msm_pipe->suballoc_bo)842fd_bo_del(msm_pipe->suballoc_bo);843msm_pipe->suballoc_bo =844fd_bo_new_ring(pipe->dev, MAX2(SUBALLOC_SIZE, align(size, 4096)));845msm_ring->offset = 0;846}847848msm_ring->u.pipe = pipe;849msm_ring->ring_bo = fd_bo_ref(msm_pipe->suballoc_bo);850msm_ring->base.refcnt = 1;851852msm_pipe->suballoc_offset = msm_ring->offset + size;853854simple_mtx_unlock(&suballoc_lock);855856return msm_ringbuffer_sp_init(msm_ring, size, _FD_RINGBUFFER_OBJECT);857}858859860