Path: blob/21.2-virgl/src/gallium/drivers/crocus/crocus_fence.c
4570 views
/*1* Copyright © 2018 Intel Corporation2*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 shall be included11* in all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS14* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER17* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING18* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER19* DEALINGS IN THE SOFTWARE.20*/2122/**23* @file crocus_fence.c24*25* Fences for driver and IPC serialisation, scheduling and synchronisation.26*/2728#include "util/u_inlines.h"29#include "intel/common/intel_gem.h"3031#include "crocus_batch.h"32#include "crocus_bufmgr.h"33#include "crocus_context.h"34#include "crocus_fence.h"35#include "crocus_screen.h"3637static uint32_t38gem_syncobj_create(int fd, uint32_t flags)39{40struct drm_syncobj_create args = {41.flags = flags,42};4344intel_ioctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &args);4546return args.handle;47}4849static void50gem_syncobj_destroy(int fd, uint32_t handle)51{52struct drm_syncobj_destroy args = {53.handle = handle,54};5556intel_ioctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &args);57}5859/**60* Make a new sync-point.61*/62struct crocus_syncobj *63crocus_create_syncobj(struct crocus_screen *screen)64{65struct crocus_syncobj *syncobj = malloc(sizeof(*syncobj));6667if (!syncobj)68return NULL;6970syncobj->handle = gem_syncobj_create(screen->fd, 0);71assert(syncobj->handle);7273pipe_reference_init(&syncobj->ref, 1);7475return syncobj;76}7778void79crocus_syncobj_destroy(struct crocus_screen *screen,80struct crocus_syncobj *syncobj)81{82gem_syncobj_destroy(screen->fd, syncobj->handle);83free(syncobj);84}8586/**87* Add a sync-point to the batch, with the given flags.88*89* \p flags One of I915_EXEC_FENCE_WAIT or I915_EXEC_FENCE_SIGNAL.90*/91void92crocus_batch_add_syncobj(struct crocus_batch *batch,93struct crocus_syncobj *syncobj, unsigned flags)94{95struct drm_i915_gem_exec_fence *fence =96util_dynarray_grow(&batch->exec_fences, struct drm_i915_gem_exec_fence, 1);9798*fence = (struct drm_i915_gem_exec_fence){99.handle = syncobj->handle,100.flags = flags,101};102103struct crocus_syncobj **store =104util_dynarray_grow(&batch->syncobjs, struct crocus_syncobj *, 1);105106*store = NULL;107crocus_syncobj_reference(batch->screen, store, syncobj);108}109110/**111* Walk through a batch's dependencies (any I915_EXEC_FENCE_WAIT syncobjs)112* and unreference any which have already passed.113*114* Sometimes the compute batch is seldom used, and accumulates references115* to stale render batches that are no longer of interest, so we can free116* those up.117*/118static void119clear_stale_syncobjs(struct crocus_batch *batch)120{121struct crocus_screen *screen = batch->screen;122123int n = util_dynarray_num_elements(&batch->syncobjs, struct crocus_syncobj *);124125assert(n == util_dynarray_num_elements(&batch->exec_fences,126struct drm_i915_gem_exec_fence));127128/* Skip the first syncobj, as it's the signalling one. */129for (int i = n - 1; i > 1; i--) {130struct crocus_syncobj **syncobj =131util_dynarray_element(&batch->syncobjs, struct crocus_syncobj *, i);132struct drm_i915_gem_exec_fence *fence =133util_dynarray_element(&batch->exec_fences,134struct drm_i915_gem_exec_fence, i);135assert(fence->flags & I915_EXEC_FENCE_WAIT);136137if (crocus_wait_syncobj(&screen->base, *syncobj, 0))138continue;139140/* This sync object has already passed, there's no need to continue141* marking it as a dependency; we can stop holding on to the reference.142*/143crocus_syncobj_reference(screen, syncobj, NULL);144145/* Remove it from the lists; move the last element here. */146struct crocus_syncobj **nth_syncobj =147util_dynarray_pop_ptr(&batch->syncobjs, struct crocus_syncobj *);148struct drm_i915_gem_exec_fence *nth_fence =149util_dynarray_pop_ptr(&batch->exec_fences,150struct drm_i915_gem_exec_fence);151152if (syncobj != nth_syncobj) {153*syncobj = *nth_syncobj;154memcpy(fence, nth_fence, sizeof(*fence));155}156}157}158159/* ------------------------------------------------------------------- */160161struct pipe_fence_handle {162struct pipe_reference ref;163164struct pipe_context *unflushed_ctx;165166struct crocus_fine_fence *fine[CROCUS_BATCH_COUNT];167};168169static void170crocus_fence_destroy(struct pipe_screen *p_screen,171struct pipe_fence_handle *fence)172{173struct crocus_screen *screen = (struct crocus_screen *)p_screen;174175for (unsigned i = 0; i < ARRAY_SIZE(fence->fine); i++)176crocus_fine_fence_reference(screen, &fence->fine[i], NULL);177178free(fence);179}180181static void182crocus_fence_reference(struct pipe_screen *p_screen,183struct pipe_fence_handle **dst,184struct pipe_fence_handle *src)185{186if (pipe_reference(&(*dst)->ref, &src->ref))187crocus_fence_destroy(p_screen, *dst);188189*dst = src;190}191192bool193crocus_wait_syncobj(struct pipe_screen *p_screen,194struct crocus_syncobj *syncobj, int64_t timeout_nsec)195{196if (!syncobj)197return false;198199struct crocus_screen *screen = (struct crocus_screen *)p_screen;200struct drm_syncobj_wait args = {201.handles = (uintptr_t)&syncobj->handle,202.count_handles = 1,203.timeout_nsec = timeout_nsec,204};205return intel_ioctl(screen->fd, DRM_IOCTL_SYNCOBJ_WAIT, &args);206}207208static void209crocus_fence_flush(struct pipe_context *ctx,210struct pipe_fence_handle **out_fence, unsigned flags)211{212struct crocus_screen *screen = (void *)ctx->screen;213struct crocus_context *ice = (struct crocus_context *)ctx;214215const bool deferred = flags & PIPE_FLUSH_DEFERRED;216217if (!deferred) {218for (unsigned i = 0; i < ice->batch_count; i++)219crocus_batch_flush(&ice->batches[i]);220}221222if (!out_fence)223return;224225struct pipe_fence_handle *fence = calloc(1, sizeof(*fence));226if (!fence)227return;228229pipe_reference_init(&fence->ref, 1);230231if (deferred)232fence->unflushed_ctx = ctx;233234for (unsigned b = 0; b < ice->batch_count; b++) {235struct crocus_batch *batch = &ice->batches[b];236237if (deferred && crocus_batch_bytes_used(batch) > 0) {238struct crocus_fine_fence *fine =239crocus_fine_fence_new(batch, CROCUS_FENCE_BOTTOM_OF_PIPE);240crocus_fine_fence_reference(screen, &fence->fine[b], fine);241crocus_fine_fence_reference(screen, &fine, NULL);242} else {243/* This batch has no commands queued up (perhaps we just flushed,244* or all the commands are on the other batch). Wait for the last245* syncobj on this engine - unless it's already finished by now.246*/247if (crocus_fine_fence_signaled(batch->last_fence))248continue;249250crocus_fine_fence_reference(screen, &fence->fine[b],251batch->last_fence);252}253}254255crocus_fence_reference(ctx->screen, out_fence, NULL);256*out_fence = fence;257}258259static void260crocus_fence_await(struct pipe_context *ctx, struct pipe_fence_handle *fence)261{262struct crocus_context *ice = (struct crocus_context *)ctx;263264/* Unflushed fences from the same context are no-ops. */265if (ctx && ctx == fence->unflushed_ctx)266return;267268for (unsigned i = 0; i < ARRAY_SIZE(fence->fine); i++) {269struct crocus_fine_fence *fine = fence->fine[i];270271if (crocus_fine_fence_signaled(fine))272continue;273274for (unsigned b = 0; b < ice->batch_count; b++) {275struct crocus_batch *batch = &ice->batches[b];276277/* We're going to make any future work in this batch wait for our278* fence to have gone by. But any currently queued work doesn't279* need to wait. Flush the batch now, so it can happen sooner.280*/281crocus_batch_flush(batch);282283/* Before adding a new reference, clean out any stale ones. */284clear_stale_syncobjs(batch);285286crocus_batch_add_syncobj(batch, fine->syncobj, I915_EXEC_FENCE_WAIT);287}288}289}290291#define NSEC_PER_SEC (1000 * USEC_PER_SEC)292#define USEC_PER_SEC (1000 * MSEC_PER_SEC)293#define MSEC_PER_SEC (1000)294295static uint64_t296gettime_ns(void)297{298struct timespec current;299clock_gettime(CLOCK_MONOTONIC, ¤t);300return (uint64_t)current.tv_sec * NSEC_PER_SEC + current.tv_nsec;301}302303static uint64_t304rel2abs(uint64_t timeout)305{306if (timeout == 0)307return 0;308309uint64_t current_time = gettime_ns();310uint64_t max_timeout = (uint64_t)INT64_MAX - current_time;311312timeout = MIN2(max_timeout, timeout);313314return current_time + timeout;315}316317static bool318crocus_fence_finish(struct pipe_screen *p_screen, struct pipe_context *ctx,319struct pipe_fence_handle *fence, uint64_t timeout)320{321ctx = threaded_context_unwrap_sync(ctx);322struct crocus_context *ice = (struct crocus_context *)ctx;323struct crocus_screen *screen = (struct crocus_screen *)p_screen;324325/* If we created the fence with PIPE_FLUSH_DEFERRED, we may not have326* flushed yet. Check if our syncobj is the current batch's signalling327* syncobj - if so, we haven't flushed and need to now.328*329* The Gallium docs mention that a flush will occur if \p ctx matches330* the context the fence was created with. It may be NULL, so we check331* that it matches first.332*/333if (ctx && ctx == fence->unflushed_ctx) {334for (unsigned i = 0; i < ice->batch_count; i++) {335struct crocus_fine_fence *fine = fence->fine[i];336337if (crocus_fine_fence_signaled(fine))338continue;339340if (fine->syncobj == crocus_batch_get_signal_syncobj(&ice->batches[i]))341crocus_batch_flush(&ice->batches[i]);342}343344/* The fence is no longer deferred. */345fence->unflushed_ctx = NULL;346}347348unsigned int handle_count = 0;349uint32_t handles[ARRAY_SIZE(fence->fine)];350for (unsigned i = 0; i < ARRAY_SIZE(fence->fine); i++) {351struct crocus_fine_fence *fine = fence->fine[i];352353if (crocus_fine_fence_signaled(fine))354continue;355356handles[handle_count++] = fine->syncobj->handle;357}358359if (handle_count == 0)360return true;361362struct drm_syncobj_wait args = {363.handles = (uintptr_t)handles,364.count_handles = handle_count,365.timeout_nsec = rel2abs(timeout),366.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL367};368if (fence->unflushed_ctx) {369/* This fence had a deferred flush from another context. We can't370* safely flush it here, because the context might be bound to a371* different thread, and poking at its internals wouldn't be safe.372*373* Instead, use the WAIT_FOR_SUBMIT flag to block and hope that374* another thread submits the work.375*/376args.flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;377}378return intel_ioctl(screen->fd, DRM_IOCTL_SYNCOBJ_WAIT, &args) == 0;379}380381#ifndef SYNC_IOC_MAGIC382/* duplicated from linux/sync_file.h to avoid build-time dependency383* on new (v4.7) kernel headers. Once distro's are mostly using384* something newer than v4.7 drop this and #include <linux/sync_file.h>385* instead.386*/387struct sync_merge_data {388char name[32];389__s32 fd2;390__s32 fence;391__u32 flags;392__u32 pad;393};394395#define SYNC_IOC_MAGIC '>'396#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)397#endif398399static int400sync_merge_fd(int sync_fd, int new_fd)401{402if (sync_fd == -1)403return new_fd;404405if (new_fd == -1)406return sync_fd;407408struct sync_merge_data args = {409.name = "crocus fence",410.fd2 = new_fd,411.fence = -1,412};413414intel_ioctl(sync_fd, SYNC_IOC_MERGE, &args);415close(new_fd);416close(sync_fd);417418return args.fence;419}420421static int422crocus_fence_get_fd(struct pipe_screen *p_screen,423struct pipe_fence_handle *fence)424{425struct crocus_screen *screen = (struct crocus_screen *)p_screen;426int fd = -1;427428/* Deferred fences aren't supported. */429if (fence->unflushed_ctx)430return -1;431432for (unsigned i = 0; i < ARRAY_SIZE(fence->fine); i++) {433struct crocus_fine_fence *fine = fence->fine[i];434435if (crocus_fine_fence_signaled(fine))436continue;437438struct drm_syncobj_handle args = {439.handle = fine->syncobj->handle,440.flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE,441.fd = -1,442};443444intel_ioctl(screen->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);445fd = sync_merge_fd(fd, args.fd);446}447448if (fd == -1) {449/* Our fence has no syncobj's recorded. This means that all of the450* batches had already completed, their syncobj's had been signalled,451* and so we didn't bother to record them. But we're being asked to452* export such a fence. So export a dummy already-signalled syncobj.453*/454struct drm_syncobj_handle args = {455.flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE,456.fd = -1,457};458459args.handle = gem_syncobj_create(screen->fd, DRM_SYNCOBJ_CREATE_SIGNALED);460intel_ioctl(screen->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);461gem_syncobj_destroy(screen->fd, args.handle);462return args.fd;463}464465return fd;466}467468static void469crocus_fence_create_fd(struct pipe_context *ctx, struct pipe_fence_handle **out,470int fd, enum pipe_fd_type type)471{472assert(type == PIPE_FD_TYPE_NATIVE_SYNC || type == PIPE_FD_TYPE_SYNCOBJ);473474struct crocus_screen *screen = (struct crocus_screen *)ctx->screen;475struct drm_syncobj_handle args = {476.fd = fd,477};478479if (type == PIPE_FD_TYPE_NATIVE_SYNC) {480args.flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE;481args.handle = gem_syncobj_create(screen->fd, DRM_SYNCOBJ_CREATE_SIGNALED);482}483484if (intel_ioctl(screen->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args) == -1) {485fprintf(stderr, "DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE failed: %s\n",486strerror(errno));487if (type == PIPE_FD_TYPE_NATIVE_SYNC)488gem_syncobj_destroy(screen->fd, args.handle);489*out = NULL;490return;491}492493struct crocus_syncobj *syncobj = malloc(sizeof(*syncobj));494if (!syncobj) {495*out = NULL;496return;497}498syncobj->handle = args.handle;499pipe_reference_init(&syncobj->ref, 1);500501struct crocus_fine_fence *fine = calloc(1, sizeof(*fine));502if (!fine) {503free(syncobj);504*out = NULL;505return;506}507508static const uint32_t zero = 0;509510/* Fences work in terms of crocus_fine_fence, but we don't actually have a511* seqno for an imported fence. So, create a fake one which always512* returns as 'not signaled' so we fall back to using the sync object.513*/514fine->seqno = UINT32_MAX;515fine->map = &zero;516fine->syncobj = syncobj;517fine->flags = CROCUS_FENCE_END;518pipe_reference_init(&fine->reference, 1);519520struct pipe_fence_handle *fence = calloc(1, sizeof(*fence));521if (!fence) {522free(fine);523free(syncobj);524*out = NULL;525return;526}527pipe_reference_init(&fence->ref, 1);528fence->fine[0] = fine;529530*out = fence;531}532533static void534crocus_fence_signal(struct pipe_context *ctx, struct pipe_fence_handle *fence)535{536struct crocus_context *ice = (struct crocus_context *)ctx;537538if (ctx == fence->unflushed_ctx)539return;540541for (unsigned b = 0; b < ice->batch_count; b++) {542for (unsigned i = 0; i < ARRAY_SIZE(fence->fine); i++) {543struct crocus_fine_fence *fine = fence->fine[i];544545/* already signaled fence skipped */546if (crocus_fine_fence_signaled(fine))547continue;548549ice->batches[b].contains_fence_signal = true;550crocus_batch_add_syncobj(&ice->batches[b], fine->syncobj,551I915_EXEC_FENCE_SIGNAL);552}553}554}555556void557crocus_init_screen_fence_functions(struct pipe_screen *screen)558{559screen->fence_reference = crocus_fence_reference;560screen->fence_finish = crocus_fence_finish;561screen->fence_get_fd = crocus_fence_get_fd;562}563564void565crocus_init_context_fence_functions(struct pipe_context *ctx)566{567ctx->flush = crocus_fence_flush;568ctx->create_fence_fd = crocus_fence_create_fd;569ctx->fence_server_sync = crocus_fence_await;570ctx->fence_server_signal = crocus_fence_signal;571}572573574