Path: blob/21.2-virgl/src/virtio/vulkan/vn_ring.c
4560 views
/*1* Copyright 2021 Google LLC2* SPDX-License-Identifier: MIT3*/45#include "vn_ring.h"67#include "vn_renderer.h"89/* must be power-of-two */10#define VN_RING_BUFFER_SIZE (1u << 11)11#define VN_RING_BUFFER_MASK (VN_RING_BUFFER_SIZE - 1)1213enum vn_ring_status_flag {14VN_RING_STATUS_IDLE = 1u << 0,15};1617static uint32_t18vn_ring_load_head(const struct vn_ring *ring)19{20/* the renderer is expected to store the head with memory_order_release,21* forming a release-acquire ordering22*/23return atomic_load_explicit(ring->shared.head, memory_order_acquire);24}2526static void27vn_ring_store_tail(struct vn_ring *ring)28{29/* the renderer is expected to load the tail with memory_order_acquire,30* forming a release-acquire ordering31*/32return atomic_store_explicit(ring->shared.tail, ring->cur,33memory_order_release);34}3536static uint32_t37vn_ring_load_status(const struct vn_ring *ring)38{39/* this must be called and ordered after vn_ring_store_tail */40return atomic_load_explicit(ring->shared.status, memory_order_seq_cst);41}4243static void44vn_ring_write_buffer(struct vn_ring *ring, const void *data, size_t size)45{46assert(ring->cur + size - vn_ring_load_head(ring) <= VN_RING_BUFFER_SIZE);4748const size_t offset = ring->cur & VN_RING_BUFFER_MASK;49if (offset + size <= VN_RING_BUFFER_SIZE) {50memcpy(ring->shared.buffer + offset, data, size);51} else {52const size_t s = VN_RING_BUFFER_SIZE - offset;53memcpy(ring->shared.buffer + offset, data, s);54memcpy(ring->shared.buffer, data + s, size - s);55}5657ring->cur += size;58}5960static bool61vn_ring_ge_seqno(const struct vn_ring *ring, uint32_t a, uint32_t b)62{63/* this can return false negative when not called fast enough (e.g., when64* called once every couple hours), but following calls with larger a's65* will correct itself66*67* TODO use real seqnos?68*/69if (a >= b)70return ring->cur >= a || ring->cur < b;71else72return ring->cur >= a && ring->cur < b;73}7475static void76vn_ring_retire_submits(struct vn_ring *ring, uint32_t seqno)77{78list_for_each_entry_safe(struct vn_ring_submit, submit, &ring->submits,79head) {80if (!vn_ring_ge_seqno(ring, seqno, submit->seqno))81break;8283for (uint32_t i = 0; i < submit->shmem_count; i++)84vn_renderer_shmem_unref(ring->renderer, submit->shmems[i]);8586list_del(&submit->head);87list_add(&submit->head, &ring->free_submits);88}89}9091static uint32_t92vn_ring_wait_seqno(const struct vn_ring *ring, uint32_t seqno)93{94/* A renderer wait incurs several hops and the renderer might poll95* repeatedly anyway. Let's just poll here.96*/97uint32_t iter = 0;98do {99const uint32_t head = vn_ring_load_head(ring);100if (vn_ring_ge_seqno(ring, head, seqno))101return head;102vn_relax(&iter);103} while (true);104}105106static uint32_t107vn_ring_wait_space(const struct vn_ring *ring, uint32_t size)108{109assert(size <= VN_RING_BUFFER_SIZE);110111/* see the reasoning in vn_ring_wait_seqno */112uint32_t iter = 0;113do {114const uint32_t head = vn_ring_load_head(ring);115if (ring->cur + size - head <= VN_RING_BUFFER_SIZE)116return head;117vn_relax(&iter);118} while (true);119}120121void122vn_ring_get_layout(size_t extra_size, struct vn_ring_layout *layout)123{124/* this can be changed/extended quite freely */125struct layout {126uint32_t head __attribute__((aligned(64)));127uint32_t tail __attribute__((aligned(64)));128uint32_t status __attribute__((aligned(64)));129130uint8_t buffer[] __attribute__((aligned(64)));131};132const size_t buf_size = VN_RING_BUFFER_SIZE;133134assert(buf_size && util_is_power_of_two_or_zero(buf_size));135136layout->head_offset = offsetof(struct layout, head);137layout->tail_offset = offsetof(struct layout, tail);138layout->status_offset = offsetof(struct layout, status);139140layout->buffer_offset = offsetof(struct layout, buffer);141layout->buffer_size = buf_size;142143layout->extra_offset = layout->buffer_offset + layout->buffer_size;144layout->extra_size = extra_size;145146layout->shmem_size = layout->extra_offset + layout->extra_size;147}148149void150vn_ring_init(struct vn_ring *ring,151struct vn_renderer *renderer,152const struct vn_ring_layout *layout,153void *shared)154{155memset(ring, 0, sizeof(*ring));156memset(shared, 0, layout->shmem_size);157158ring->renderer = renderer;159160ring->shared.head = shared + layout->head_offset;161ring->shared.tail = shared + layout->tail_offset;162ring->shared.status = shared + layout->status_offset;163ring->shared.buffer = shared + layout->buffer_offset;164ring->shared.extra = shared + layout->extra_offset;165166list_inithead(&ring->submits);167list_inithead(&ring->free_submits);168}169170void171vn_ring_fini(struct vn_ring *ring)172{173vn_ring_retire_submits(ring, ring->cur);174assert(list_is_empty(&ring->submits));175176list_for_each_entry_safe(struct vn_ring_submit, submit,177&ring->free_submits, head)178free(submit);179}180181struct vn_ring_submit *182vn_ring_get_submit(struct vn_ring *ring, uint32_t shmem_count)183{184const uint32_t min_shmem_count = 2;185struct vn_ring_submit *submit;186187/* TODO this could be simplified if we could omit shmem_count */188if (shmem_count <= min_shmem_count &&189!list_is_empty(&ring->free_submits)) {190submit =191list_first_entry(&ring->free_submits, struct vn_ring_submit, head);192list_del(&submit->head);193} else {194shmem_count = MAX2(shmem_count, min_shmem_count);195submit =196malloc(sizeof(*submit) + sizeof(submit->shmems[0]) * shmem_count);197}198199return submit;200}201202bool203vn_ring_submit(struct vn_ring *ring,204struct vn_ring_submit *submit,205const void *cs_data,206size_t cs_size,207uint32_t *seqno)208{209const uint32_t cur_seqno = vn_ring_wait_space(ring, cs_size);210vn_ring_write_buffer(ring, cs_data, cs_size);211vn_ring_store_tail(ring);212const bool notify = vn_ring_load_status(ring) & VN_RING_STATUS_IDLE;213214vn_ring_retire_submits(ring, cur_seqno);215216submit->seqno = ring->cur;217list_addtail(&submit->head, &ring->submits);218219*seqno = submit->seqno;220return notify;221}222223/**224* This is thread-safe.225*/226void227vn_ring_wait(const struct vn_ring *ring, uint32_t seqno)228{229vn_ring_wait_seqno(ring, seqno);230}231232233