blob: d6be03be8e1c338c8232f5db62a8ac0c2740a0ff [file] [log] [blame]
/*
* Copyright 2022 Google LLC
* SPDX-License-Identifier: MIT
*/
#include <poll.h>
#include <string.h>
#include "virgl_context.h"
#include "virgl_util.h"
#include "util/os_file.h"
#include "util/u_atomic.h"
#include "util/u_thread.h"
#include "drm_fence.h"
#include "drm_util.h"
/**
* Tracking for a single fence on a timeline
*/
struct drm_fence {
int fd;
uint32_t flags;
void *fence_cookie;
struct list_head node;
};
static void
drm_fence_destroy(struct drm_fence *fence)
{
close(fence->fd);
list_del(&fence->node);
free(fence);
}
static struct drm_fence *
drm_fence_create(int fd, uint32_t flags, void *fence_cookie)
{
struct drm_fence *fence = calloc(1, sizeof(*fence));
if (!fence)
return NULL;
fence->fd = os_dupfd_cloexec(fd);
if (fence->fd < 0) {
free(fence);
return NULL;
}
fence->flags = flags;
fence->fence_cookie = fence_cookie;
return fence;
}
static int
thread_sync(void *arg)
{
struct drm_timeline *timeline = arg;
u_thread_setname(timeline->name);
mtx_lock(&timeline->fence_mutex);
while (!timeline->stop_sync_thread) {
if (list_is_empty(&timeline->pending_fences)) {
if (cnd_wait(&timeline->fence_cond, &timeline->fence_mutex))
drm_log("error waiting on fence condition");
continue;
}
struct drm_fence *fence =
list_first_entry(&timeline->pending_fences, struct drm_fence, node);
int ret;
mtx_unlock(&timeline->fence_mutex);
ret = poll(&(struct pollfd){fence->fd, POLLIN}, 1, -1); /* wait forever */
mtx_lock(&timeline->fence_mutex);
if (ret == 1) {
drm_dbg("fence signaled: %p (%p)", fence, fence->fence_cookie);
timeline->vctx->fence_retire(timeline->vctx, timeline->ring_idx,
(uintptr_t)fence->fence_cookie);
write_eventfd(timeline->eventfd, 1);
drm_fence_destroy(fence);
} else if (ret != 0) {
drm_log("poll failed: %s", strerror(errno));
}
}
mtx_unlock(&timeline->fence_mutex);
return 0;
}
void
drm_timeline_init(struct drm_timeline *timeline, struct virgl_context *vctx,
const char *name, int eventfd, int ring_idx)
{
timeline->vctx = vctx;
timeline->name = name;
timeline->eventfd = eventfd;
timeline->ring_idx = ring_idx;
timeline->last_fence_fd = -1;
list_inithead(&timeline->pending_fences);
mtx_init(&timeline->fence_mutex, mtx_plain);
cnd_init(&timeline->fence_cond);
timeline->sync_thread = u_thread_create(thread_sync, timeline);
}
void
drm_timeline_fini(struct drm_timeline *timeline)
{
/* signal thread_sync to shutdown: */
mtx_lock(&timeline->fence_mutex);
timeline->stop_sync_thread = true;
cnd_signal(&timeline->fence_cond);
mtx_unlock(&timeline->fence_mutex);
/* wait for thread_sync to exit: */
thrd_join(timeline->sync_thread, NULL);
if (timeline->last_fence_fd != -1)
close(timeline->last_fence_fd);
/* cleanup remaining fences: */
list_for_each_entry_safe (struct drm_fence, fence, &timeline->pending_fences, node) {
drm_fence_destroy(fence);
}
cnd_destroy(&timeline->fence_cond);
mtx_destroy(&timeline->fence_mutex);
}
int
drm_timeline_submit_fence(struct drm_timeline *timeline, uint32_t flags,
void *fence_cookie)
{
if (timeline->last_fence_fd == -1)
return -EINVAL;
struct drm_fence *fence =
drm_fence_create(timeline->last_fence_fd, flags, fence_cookie);
if (!fence)
return -ENOMEM;
drm_dbg("fence: %p (%p)", fence, fence->fence_cookie);
mtx_lock(&timeline->fence_mutex);
list_addtail(&fence->node, &timeline->pending_fences);
cnd_signal(&timeline->fence_cond);
mtx_unlock(&timeline->fence_mutex);
return 0;
}
/* takes ownership of the fd */
void
drm_timeline_set_last_fence_fd(struct drm_timeline *timeline, int fd)
{
if (timeline->last_fence_fd != -1)
close(timeline->last_fence_fd);
timeline->last_fence_fd = fd;
}