blob: 475a8a619e6e62986e5c82ca1bfaa9d996608ae9 [file] [log] [blame]
/*
* Copyright © 2024 The Fuchsia Authors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "vk_zircon_syncobj.h"
#include <string.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/port.h>
#include "vk_device.h"
#include "vk_log.h"
#include "vk_util.h"
static struct vk_zircon_syncobj *
to_zircon_syncobj(struct vk_sync *sync)
{
assert(vk_sync_type_is_zircon_syncobj(sync->type));
return container_of(sync, struct vk_zircon_syncobj, base);
}
static VkResult vk_zircon_syncobj_init(struct vk_device *device,
struct vk_sync *sync,
uint64_t initial_value) {
struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
zx_handle_t handle = (zx_handle_t) initial_value;
assert((sync->flags & VK_SYNC_IS_TIMELINE) == 0);
/* |initial_value| is overloaded to convey zx_handle_t handles. */
/* If it's set and not 1, it's a zx_handle_t. */
if (initial_value && initial_value != 1) {
assert((initial_value >> 32) == 0);
/* Close existing handle if it exists. */
if (sobj->semaphore != ZX_HANDLE_INVALID) {
if (zx_handle_close(sobj->semaphore) != ZX_OK) {
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to close handle: %m");
}
}
/* Duplicate and store |handle| into |sobj->semaphore|. */
zx_status_t status =
zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS,
(zx_handle_t *) &sobj->semaphore);
if(status != ZX_OK) {
if(status == ZX_ERR_BAD_HANDLE)
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to initialize, bad Zircon handle: %m");
else
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to initialize: %m");
}
/* Close original |handle| to invalidate it. */
if (zx_handle_close(handle) != ZX_OK) {
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to close handle: %m");
}
} else {
zx_status_t status = zx_event_create(0, (zx_handle_t *) &sobj->semaphore);
if (status != ZX_OK) {
assert(status == ZX_ERR_NO_MEMORY);
return vk_errorf(device, VK_ERROR_OUT_OF_HOST_MEMORY,
"Unable to create semaphore: %m");
}
}
if (initial_value == 1) {
zx_status_t status =
zx_object_signal((zx_handle_t) sobj->semaphore, 0u, ZX_EVENT_SIGNALED);
if(status != ZX_OK) {
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to signal semaphore: %m");
}
}
return VK_SUCCESS;
}
void
vk_zircon_syncobj_finish(struct vk_device *device,
struct vk_sync *sync)
{
struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
if (sobj->semaphore == ZX_HANDLE_INVALID) {
vk_logi(VK_LOG_OBJS(device), "Attempt to close invalid Zircon event handle.\n");
return;
}
zx_status_t status = zx_handle_close(sobj->semaphore);
if (status == ZX_ERR_BAD_HANDLE) {
vk_loge(VK_LOG_OBJS(device), "Attempt to close bad Zircon event handle.\n");
return;
}
assert(status == ZX_OK);
sobj->semaphore = ZX_HANDLE_INVALID;
}
static VkResult
vk_zircon_syncobj_signal(struct vk_device *device,
struct vk_sync *sync,
uint64_t value)
{
struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
if (sobj->semaphore == ZX_HANDLE_INVALID) {
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Attempt to signal invalid Zircon event handle: %m");
}
zx_status_t status = zx_object_signal(sobj->semaphore, 0u, ZX_EVENT_SIGNALED);
switch(status) {
case ZX_OK:
return VK_SUCCESS;
case ZX_ERR_BAD_HANDLE:
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to signal semaphore, bad Zircon handle: %m");
default:
return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to signal semaphore: %m");
}
}
static VkResult
vk_zircon_syncobj_reset(struct vk_device *device,
struct vk_sync *sync)
{
struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
if (sobj->semaphore == ZX_HANDLE_INVALID) {
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Attempt to reset invalid Zircon event handle: %m");
}
zx_status_t status = zx_object_signal(sobj->semaphore, ZX_EVENT_SIGNALED, 0u);
switch(status) {
case ZX_OK:
return VK_SUCCESS;
case ZX_ERR_BAD_HANDLE:
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to reset semaphore, bad Zircon handle: %m");
default:
return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to reset semaphore: %m");
}
}
static VkResult
vk_zircon_syncobj_wait_many(struct vk_device *device,
uint32_t wait_count,
const struct vk_sync_wait *waits,
enum vk_sync_wait_flags wait_flags,
uint64_t abs_timeout_ns)
{
assert((wait_flags & VK_SYNC_WAIT_PENDING) == 0);
if(!wait_count) return VK_SUCCESS;
/* Syncobj timeouts are signed. */
abs_timeout_ns = MIN2(abs_timeout_ns, (uint64_t)ZX_TIME_INFINITE);
/* Create port on which to monitor signaled semaphores. */
zx_handle_t port = ZX_HANDLE_INVALID;
zx_status_t status = zx_port_create(0, &port);
if(status != ZX_OK) {
return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to create port: %m");
}
/* Enqueue all semaphores (events) on the port. */
for (uint32_t i = 0; i < wait_count; i++) {
uint64_t semaphore = to_zircon_syncobj(waits[i].sync)->semaphore;
status = zx_object_wait_async(semaphore, port, i /* key */, ZX_EVENT_SIGNALED,
0 /* options */);
if (status != ZX_OK) {
return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to queue semaphore on port: %m");
}
}
status = ZX_OK;
const bool kWaitAll = (wait_flags & VK_SYNC_WAIT_ANY) == 0;
zx_port_packet_t packet = {0};
if(kWaitAll) {
const size_t kMaxMasks = 16;
uint64_t masks[kMaxMasks];
uint64_t *masks_base = NULL;
uint64_t *masks_ptr = masks;
const uint32_t num_masks = ((wait_count - 1) / 64) + 1;
if(num_masks > kMaxMasks) {
masks_base = (uint64_t *) malloc(num_masks * sizeof(uint64_t));
if(!masks_base) {
return vk_errorf(device, VK_ERROR_OUT_OF_HOST_MEMORY, "Masks alloc failed: %m");
}
masks_ptr = masks_base;
}
memset(masks_ptr, 0xFF, num_masks * sizeof(uint64_t));
/* Clear all unused bits of the last mask. */
*(masks_ptr + num_masks - 1) >>= (64 - (wait_count % 64));
int cleared_masks = 0;
while(cleared_masks < num_masks) {
status = zx_port_wait(port, abs_timeout_ns, &packet);
const uint32_t mask_index = packet.key / 64;
switch(status) {
case ZX_OK:
/* Knock out the bit for the current signal. */
*(masks_ptr + mask_index) &= ~(1 << (packet.key % 64));
for(cleared_masks = 0; cleared_masks < num_masks; cleared_masks++) {
if(*(masks_ptr + cleared_masks)) {
break;
}
}
break;
default:
cleared_masks = num_masks;
break;
}
}
if(masks_base) free(masks_base);
} else {
/* Wait for any event signal. */
status = zx_port_wait(port, abs_timeout_ns, &packet);
}
switch(status) {
case ZX_OK:
return VK_SUCCESS;
case ZX_ERR_TIMED_OUT:
return vk_errorf(device, VK_TIMEOUT, "zx_port_wait timed out: %m");
default:
return vk_errorf(device, VK_ERROR_UNKNOWN, "zx_port_wait failed: %m");
}
}
static VkResult
vk_zircon_syncobj_import_zircon_handle(struct vk_device *device,
struct vk_sync *sync,
uint32_t handle)
{
struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
zx_handle_t semaphore = ZX_HANDLE_INVALID;
zx_status_t status =
zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &semaphore);
if(status != ZX_OK) {
if(status == ZX_ERR_BAD_HANDLE)
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to import, bad Zircon handle: %m");
else
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to import handle: %m");
}
if (zx_handle_close(handle) != ZX_OK) {
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to import handle: %m");
}
status = zx_handle_close((zx_handle_t) sobj->semaphore);
switch(status) {
case ZX_OK:
sobj->semaphore = (uint64_t) semaphore;
return VK_SUCCESS;
case ZX_ERR_BAD_HANDLE:
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to import, bad Zircon handle: %m");
default:
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to import handle: %m");
}
}
static VkResult
vk_zircon_syncobj_export_zircon_handle(struct vk_device *device,
struct vk_sync *sync,
uint32_t* handle_out)
{
struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
if (sobj->semaphore == ZX_HANDLE_INVALID) {
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Attempt to export invalid Zircon event handle: %m");
}
zx_status_t status = zx_handle_duplicate(sobj->semaphore,
ZX_RIGHT_SAME_RIGHTS, handle_out);
switch(status) {
case ZX_OK:
return VK_SUCCESS;
case ZX_ERR_BAD_HANDLE:
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to export, bad Zircon handle: %m");
default:
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to export handle: %m");
}
}
static VkResult
vk_zircon_syncobj_move(struct vk_device *device,
struct vk_sync *dst,
struct vk_sync *src)
{
struct vk_zircon_syncobj *dst_sobj = to_zircon_syncobj(dst);
struct vk_zircon_syncobj *src_sobj = to_zircon_syncobj(src);
if(src_sobj->semaphore == ZX_HANDLE_INVALID) {
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to move invalid handle: %m");
}
zx_handle_t handle_out = ZX_HANDLE_INVALID;
zx_status_t status = zx_handle_duplicate(src_sobj->semaphore,
ZX_RIGHT_SAME_RIGHTS, &handle_out);
if(status != ZX_OK) {
if(status == ZX_ERR_BAD_HANDLE)
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Unable to move, bad Zircon handle: %m");
else
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to move handle: %m");
}
if (zx_handle_close((zx_handle_t) src_sobj->semaphore) != ZX_OK) {
return vk_errorf(device, VK_ERROR_UNKNOWN,
"Unable to close (move) src semaphore handle: %m");
}
src_sobj->semaphore = ZX_HANDLE_INVALID;
*dst_sobj = *src_sobj;
dst_sobj->semaphore = (uint64_t) handle_out;
return VK_SUCCESS;
}
struct vk_sync_type
vk_zircon_syncobj_get_type(void)
{
struct vk_sync_type type = {
.size = sizeof(struct vk_zircon_syncobj),
.features = VK_SYNC_FEATURE_BINARY |
VK_SYNC_FEATURE_GPU_WAIT |
VK_SYNC_FEATURE_CPU_RESET |
VK_SYNC_FEATURE_CPU_SIGNAL |
VK_SYNC_FEATURE_CPU_WAIT |
VK_SYNC_FEATURE_WAIT_PENDING |
VK_SYNC_FEATURE_WAIT_ANY,
.init = vk_zircon_syncobj_init,
.finish = vk_zircon_syncobj_finish,
.move = vk_zircon_syncobj_move,
.signal = vk_zircon_syncobj_signal,
.reset = vk_zircon_syncobj_reset,
.wait_many = vk_zircon_syncobj_wait_many,
.import_zircon_handle = vk_zircon_syncobj_import_zircon_handle,
.export_zircon_handle = vk_zircon_syncobj_export_zircon_handle,
};
return type;
}