diff --git a/src/venus/vkr_ring.c b/src/venus/vkr_ring.c index 223ff84..34a3a0f 100644 --- a/src/venus/vkr_ring.c +++ b/src/venus/vkr_ring.c @@ -15,13 +15,59 @@ enum vkr_ring_status_flag { VKR_RING_STATUS_IDLE = 1u << 0, }; +/* callers must make sure they do not seek to end-of-resource or beyond */ +static const struct iovec * +seek_resource(const struct virgl_resource *res, + int base_iov_index, + size_t offset, + int *out_iov_index, + size_t *out_iov_offset) +{ + const struct iovec *iov = &res->iov[base_iov_index]; + assert(iov - res->iov < res->iov_count); + while (offset >= iov->iov_len) { + offset -= iov->iov_len; + iov++; + assert(iov - res->iov < res->iov_count); + } + + *out_iov_index = iov - res->iov; + *out_iov_offset = offset; + + return iov; +} + +static void * +get_resource_pointer(const struct virgl_resource *res, int base_iov_index, size_t offset) +{ + const struct iovec *iov = + seek_resource(res, base_iov_index, offset, &base_iov_index, &offset); + return (uint8_t *)iov->iov_base + offset; +} + +static bool +vkr_ring_init_control(struct vkr_ring *ring, const struct vkr_ring_layout *layout) +{ + struct vkr_ring_control *ctrl = &ring->control; + + ctrl->head = get_resource_pointer(layout->resource, 0, layout->head.begin); + ctrl->tail = get_resource_pointer(layout->resource, 0, layout->tail.begin); + ctrl->status = get_resource_pointer(layout->resource, 0, layout->status.begin); + + /* we will manage head and status, and we expect them to be 0 initially */ + if (*ctrl->head || *ctrl->status) + return false; + + return true; +} + static void vkr_ring_store_head(struct vkr_ring *ring) { /* the renderer is expected to load the head with memory_order_acquire, * forming a release-acquire ordering */ - atomic_store_explicit(ring->shared.head, ring->cur, memory_order_release); + atomic_store_explicit(ring->control.head, ring->cur, memory_order_release); } static uint32_t @@ -30,13 +76,13 @@ vkr_ring_load_tail(const struct vkr_ring *ring) /* the driver is expected to store the tail with memory_order_release, * forming a release-acquire ordering */ - return atomic_load_explicit(ring->shared.tail, memory_order_acquire); + return atomic_load_explicit(ring->control.tail, memory_order_acquire); } static void vkr_ring_store_status(struct vkr_ring *ring, uint32_t status) { - atomic_store_explicit(ring->shared.status, status, memory_order_seq_cst); + atomic_store_explicit(ring->control.status, status, memory_order_seq_cst); } static void @@ -69,12 +115,14 @@ vkr_ring_create(const struct vkr_ring_layout *layout, ring->resource = layout->resource; + if (!vkr_ring_init_control(ring, layout)) { + free(ring); + return NULL; + } + uint8_t *shared = layout->resource->iov[0].iov_base; #define ring_attach_shared(member) \ ring->shared.member = (void *)(shared + layout->member.begin) - ring_attach_shared(head); - ring_attach_shared(tail); - ring_attach_shared(status); ring_attach_shared(buffer); ring_attach_shared(extra); #undef ring_attach_shared @@ -84,12 +132,6 @@ vkr_ring_create(const struct vkr_ring_layout *layout, ring->buffer_mask = ring->buffer_size - 1; ring->extra_size = vkr_region_size(&layout->extra); - /* we will manage head and status, and we expect them to be 0 initially */ - if (*ring->shared.head || *ring->shared.status) { - free(ring); - return NULL; - } - ring->cmd = malloc(ring->buffer_size); if (!ring->cmd) { free(ring); diff --git a/src/venus/vkr_ring.h b/src/venus/vkr_ring.h index 6cd0050..b37fda6 100644 --- a/src/venus/vkr_ring.h +++ b/src/venus/vkr_ring.h @@ -22,13 +22,22 @@ struct vkr_ring_layout { }; static_assert(ATOMIC_INT_LOCK_FREE == 2 && sizeof(atomic_uint) == 4, - "vkr_ring_shared requires lock-free 32-bit atomic_uint"); - -/* pointers to a ring in a virgl_resource */ -struct vkr_ring_shared { + "vkr_ring_control requires lock-free 32-bit atomic_uint"); + +/* the control region of a ring */ +struct vkr_ring_control { + /* Pointers to ring head, tail, and status. + * + * Clients increment the tail after commands are added. We increment the + * head after commands are executed. The status is updated when there is a + * status change to the ring thread. + */ volatile atomic_uint *head; const volatile atomic_uint *tail; volatile atomic_uint *status; +}; + +struct vkr_ring_shared { const void *buffer; void *extra; @@ -39,7 +48,10 @@ struct vkr_ring { vkr_object_id id; struct list_head head; + /* ring regions */ struct virgl_resource *resource; + struct vkr_ring_control control; + struct vkr_ring_shared shared; uint32_t buffer_size; uint32_t buffer_mask; @@ -48,6 +60,7 @@ struct vkr_ring { size_t extra_size; + /* ring thread */ struct virgl_context *context; uint64_t idle_timeout;