summaryrefslogtreecommitdiff
path: root/src/virtio/vulkan/vn_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/virtio/vulkan/vn_common.c')
-rw-r--r--src/virtio/vulkan/vn_common.c362
1 files changed, 344 insertions, 18 deletions
diff --git a/src/virtio/vulkan/vn_common.c b/src/virtio/vulkan/vn_common.c
index e728a7971d2..4d22f184487 100644
--- a/src/virtio/vulkan/vn_common.c
+++ b/src/virtio/vulkan/vn_common.c
@@ -12,32 +12,86 @@
#include <stdarg.h>
-#include "util/debug.h"
#include "util/log.h"
#include "util/os_misc.h"
+#include "util/u_debug.h"
+#include "venus-protocol/vn_protocol_driver_info.h"
#include "vk_enum_to_str.h"
+#include "vn_instance.h"
+#include "vn_ring.h"
+
static const struct debug_control vn_debug_options[] = {
+ /* clang-format off */
{ "init", VN_DEBUG_INIT },
{ "result", VN_DEBUG_RESULT },
{ "vtest", VN_DEBUG_VTEST },
{ "wsi", VN_DEBUG_WSI },
+ { "no_abort", VN_DEBUG_NO_ABORT },
+ { "log_ctx_info", VN_DEBUG_LOG_CTX_INFO },
+ { "cache", VN_DEBUG_CACHE },
+ { "no_sparse", VN_DEBUG_NO_SPARSE },
+ { "no_gpl", VN_DEBUG_NO_GPL },
+ { NULL, 0 },
+ /* clang-format on */
+};
+
+static const struct debug_control vn_perf_options[] = {
+ /* clang-format off */
+ { "no_async_set_alloc", VN_PERF_NO_ASYNC_SET_ALLOC },
+ { "no_async_buffer_create", VN_PERF_NO_ASYNC_BUFFER_CREATE },
+ { "no_async_queue_submit", VN_PERF_NO_ASYNC_QUEUE_SUBMIT },
+ { "no_event_feedback", VN_PERF_NO_EVENT_FEEDBACK },
+ { "no_fence_feedback", VN_PERF_NO_FENCE_FEEDBACK },
+ { "no_memory_suballoc", VN_PERF_NO_MEMORY_SUBALLOC },
+ { "no_cmd_batching", VN_PERF_NO_CMD_BATCHING },
+ { "no_semaphore_feedback", VN_PERF_NO_SEMAPHORE_FEEDBACK },
+ { "no_query_feedback", VN_PERF_NO_QUERY_FEEDBACK },
+ { "no_async_mem_alloc", VN_PERF_NO_ASYNC_MEM_ALLOC },
+ { "no_tiled_wsi_image", VN_PERF_NO_TILED_WSI_IMAGE },
+ { "no_multi_ring", VN_PERF_NO_MULTI_RING },
+ { "no_async_image_create", VN_PERF_NO_ASYNC_IMAGE_CREATE },
+ { "no_async_image_format", VN_PERF_NO_ASYNC_IMAGE_FORMAT },
{ NULL, 0 },
+ /* clang-format on */
};
-uint64_t vn_debug;
+uint64_t vn_next_obj_id = 1;
+struct vn_env vn_env;
static void
-vn_debug_init_once(void)
+vn_env_init_once(void)
{
- vn_debug = parse_debug_string(os_get_option("VN_DEBUG"), vn_debug_options);
+ vn_env.debug =
+ parse_debug_string(os_get_option("VN_DEBUG"), vn_debug_options);
+ vn_env.perf =
+ parse_debug_string(os_get_option("VN_PERF"), vn_perf_options);
}
void
-vn_debug_init(void)
+vn_env_init(void)
{
static once_flag once = ONCE_FLAG_INIT;
- call_once(&once, vn_debug_init_once);
+ call_once(&once, vn_env_init_once);
+
+ /* log per VkInstance creation */
+ if (VN_DEBUG(INIT)) {
+ vn_log(NULL,
+ "vn_env is as below:"
+ "\n\tdebug = 0x%" PRIx64 ""
+ "\n\tperf = 0x%" PRIx64 "",
+ vn_env.debug, vn_env.perf);
+ }
+}
+
+void
+vn_trace_init(void)
+{
+#if DETECT_OS_ANDROID
+ atrace_init();
+#else
+ util_cpu_trace_init();
+#endif
}
void
@@ -61,29 +115,301 @@ vn_log_result(struct vn_instance *instance,
return result;
}
+uint32_t
+vn_extension_get_spec_version(const char *name)
+{
+ const int32_t index = vn_info_extension_index(name);
+ return index >= 0 ? vn_info_extension_get(index)->spec_version : 0;
+}
+
+static inline bool
+vn_watchdog_timeout(const struct vn_watchdog *watchdog)
+{
+ return !watchdog->alive;
+}
+
+static inline void
+vn_watchdog_release(struct vn_watchdog *watchdog)
+{
+ if (vn_gettid() == watchdog->tid) {
+ watchdog->tid = 0;
+ mtx_unlock(&watchdog->mutex);
+ }
+}
+
+static bool
+vn_watchdog_acquire(struct vn_watchdog *watchdog, bool alive)
+{
+ pid_t tid = vn_gettid();
+ if (!watchdog->tid && tid != watchdog->tid &&
+ mtx_trylock(&watchdog->mutex) == thrd_success) {
+ /* register as the only waiting thread that monitors the ring. */
+ watchdog->tid = tid;
+ }
+
+ if (tid != watchdog->tid)
+ return false;
+
+ watchdog->alive = alive;
+ return true;
+}
+
void
-vn_relax(uint32_t *iter, const char *reason)
+vn_relax_fini(struct vn_relax_state *state)
+{
+ vn_watchdog_release(&state->instance->ring.watchdog);
+}
+
+static inline const char *
+vn_relax_reason_string(enum vn_relax_reason reason)
{
- /* Yield for the first 2^busy_wait_order times and then sleep for
- * base_sleep_us microseconds for the same number of times. After that,
- * keep doubling both sleep length and count.
+ /* deliberately avoid default case for -Wswitch to catch upon compile */
+ switch (reason) {
+ case VN_RELAX_REASON_RING_SEQNO:
+ return "ring seqno";
+ case VN_RELAX_REASON_TLS_RING_SEQNO:
+ return "tls ring seqno";
+ case VN_RELAX_REASON_RING_SPACE:
+ return "ring space";
+ case VN_RELAX_REASON_FENCE:
+ return "fence";
+ case VN_RELAX_REASON_SEMAPHORE:
+ return "semaphore";
+ case VN_RELAX_REASON_QUERY:
+ return "query";
+ }
+ return "";
+}
+
+static struct vn_relax_profile
+vn_relax_get_profile(enum vn_relax_reason reason)
+{
+ /* This is the helper to map a vn_relax_reason to a profile. For new
+ * profiles added, we MUST also update the pre-calculated "first_warn_time"
+ * in vn_watchdog_init() if the "first warn" comes sooner.
*/
- const uint32_t busy_wait_order = 4;
- const uint32_t base_sleep_us = 10;
- const uint32_t warn_order = 12;
+ /* deliberately avoid default case for -Wswitch to catch upon compile */
+ switch (reason) {
+ case VN_RELAX_REASON_RING_SEQNO:
+ /* warn every 4096 iters after having already slept ~3.5s:
+ * (yielded 255 times)
+ * stuck in wait with iter at 4096 (3.5s slept already)
+ * stuck in wait with iter at 8192 (14s slept already)
+ * stuck in wait with iter at 12288 (35s slept already)
+ * ...
+ * aborting after 895s
+ */
+ return (struct vn_relax_profile){
+ .base_sleep_us = 160,
+ .busy_wait_order = 8,
+ .warn_order = 12,
+ .abort_order = 16,
+ };
+ case VN_RELAX_REASON_TLS_RING_SEQNO:
+ case VN_RELAX_REASON_RING_SPACE:
+ case VN_RELAX_REASON_FENCE:
+ case VN_RELAX_REASON_SEMAPHORE:
+ case VN_RELAX_REASON_QUERY:
+ /* warn every 1024 iters after having already slept ~3.5s:
+ * (yielded 15 times)
+ * stuck in wait with iter at 1024 (3.5s slept already)
+ * stuck in wait with iter at 2048 (14s slept already)
+ * stuck in wait with iter at 3072 (35s slept already)
+ * ...
+ * aborting after 895s
+ */
+ return (struct vn_relax_profile){
+ .base_sleep_us = 160,
+ .busy_wait_order = 4,
+ .warn_order = 10,
+ .abort_order = 14,
+ };
+ }
+
+ unreachable("unhandled vn_relax_reason");
+}
+
+struct vn_relax_state
+vn_relax_init(struct vn_instance *instance, enum vn_relax_reason reason)
+{
+ struct vn_ring *ring = instance->ring.ring;
+ struct vn_watchdog *watchdog = &instance->ring.watchdog;
+ if (vn_watchdog_acquire(watchdog, true))
+ vn_ring_unset_status_bits(ring, VK_RING_STATUS_ALIVE_BIT_MESA);
+
+ return (struct vn_relax_state){
+ .instance = instance,
+ .iter = 0,
+ .profile = vn_relax_get_profile(reason),
+ .reason_str = vn_relax_reason_string(reason),
+ };
+}
+
+void
+vn_relax(struct vn_relax_state *state)
+{
+ const uint32_t base_sleep_us = state->profile.base_sleep_us;
+ const uint32_t busy_wait_order = state->profile.busy_wait_order;
+ const uint32_t warn_order = state->profile.warn_order;
+ const uint32_t abort_order = state->profile.abort_order;
+
+ uint32_t *iter = &state->iter;
(*iter)++;
if (*iter < (1 << busy_wait_order)) {
thrd_yield();
return;
}
- /* warn occasionally if we have slept at least 1.28ms for 2048 times (plus
- * another 2047 shorter sleeps)
- */
- if (unlikely(*iter % (1 << warn_order) == 0))
- vn_log(NULL, "stuck in %s wait with iter at %d", reason, *iter);
+ if (unlikely(*iter % (1 << warn_order) == 0)) {
+ struct vn_instance *instance = state->instance;
+ vn_log(instance, "stuck in %s wait with iter at %d", state->reason_str,
+ *iter);
+
+ struct vn_ring *ring = instance->ring.ring;
+ const uint32_t status = vn_ring_load_status(ring);
+ if (status & VK_RING_STATUS_FATAL_BIT_MESA) {
+ vn_log(instance, "aborting on ring fatal error at iter %d", *iter);
+ abort();
+ }
+
+ struct vn_watchdog *watchdog = &instance->ring.watchdog;
+ const bool alive = status & VK_RING_STATUS_ALIVE_BIT_MESA;
+ if (vn_watchdog_acquire(watchdog, alive))
+ vn_ring_unset_status_bits(ring, VK_RING_STATUS_ALIVE_BIT_MESA);
+
+ if (vn_watchdog_timeout(watchdog) && !VN_DEBUG(NO_ABORT)) {
+ vn_log(instance, "aborting on expired ring alive status at iter %d",
+ *iter);
+ abort();
+ }
+
+ if (*iter >= (1 << abort_order) && !VN_DEBUG(NO_ABORT)) {
+ vn_log(instance, "aborting");
+ abort();
+ }
+ }
const uint32_t shift = util_last_bit(*iter) - busy_wait_order - 1;
os_time_sleep(base_sleep_us << shift);
}
+
+struct vn_ring *
+vn_tls_get_ring(struct vn_instance *instance)
+{
+ if (VN_PERF(NO_MULTI_RING))
+ return instance->ring.ring;
+
+ struct vn_tls *tls = vn_tls_get();
+ if (unlikely(!tls)) {
+ /* only allow to fallback on missing tls */
+ return instance->ring.ring;
+ }
+
+ /* look up tls_ring owned by instance */
+ list_for_each_entry(struct vn_tls_ring, tls_ring, &tls->tls_rings,
+ tls_head) {
+ mtx_lock(&tls_ring->mutex);
+ if (tls_ring->instance == instance) {
+ mtx_unlock(&tls_ring->mutex);
+ assert(tls_ring->ring);
+ return tls_ring->ring;
+ }
+ mtx_unlock(&tls_ring->mutex);
+ }
+
+ struct vn_tls_ring *tls_ring = calloc(1, sizeof(*tls_ring));
+ if (!tls_ring)
+ return NULL;
+
+ /* keep the extra for potential roundtrip sync on tls ring */
+ static const size_t extra_size = sizeof(uint32_t);
+
+ /* only need a small ring for synchronous cmds on tls ring */
+ static const size_t buf_size = 16 * 1024;
+
+ /* single cmd can use the entire ring shmem on tls ring */
+ static const uint8_t direct_order = 0;
+
+ struct vn_ring_layout layout;
+ vn_ring_get_layout(buf_size, extra_size, &layout);
+
+ tls_ring->ring = vn_ring_create(instance, &layout, direct_order);
+ if (!tls_ring->ring) {
+ free(tls_ring);
+ return NULL;
+ }
+
+ mtx_init(&tls_ring->mutex, mtx_plain);
+ tls_ring->instance = instance;
+ list_add(&tls_ring->tls_head, &tls->tls_rings);
+ list_add(&tls_ring->vk_head, &instance->ring.tls_rings);
+
+ return tls_ring->ring;
+}
+
+void
+vn_tls_destroy_ring(struct vn_tls_ring *tls_ring)
+{
+ mtx_lock(&tls_ring->mutex);
+ if (tls_ring->ring) {
+ vn_ring_destroy(tls_ring->ring);
+ tls_ring->ring = NULL;
+ tls_ring->instance = NULL;
+ mtx_unlock(&tls_ring->mutex);
+ } else {
+ mtx_unlock(&tls_ring->mutex);
+ mtx_destroy(&tls_ring->mutex);
+ free(tls_ring);
+ }
+}
+
+static void
+vn_tls_free(void *tls)
+{
+ if (tls) {
+ list_for_each_entry_safe(struct vn_tls_ring, tls_ring,
+ &((struct vn_tls *)tls)->tls_rings, tls_head)
+ vn_tls_destroy_ring(tls_ring);
+ }
+ free(tls);
+}
+
+static tss_t vn_tls_key;
+static bool vn_tls_key_valid;
+
+static void
+vn_tls_key_create_once(void)
+{
+ vn_tls_key_valid = tss_create(&vn_tls_key, vn_tls_free) == thrd_success;
+ if (!vn_tls_key_valid && VN_DEBUG(INIT))
+ vn_log(NULL, "WARNING: failed to create vn_tls_key");
+}
+
+struct vn_tls *
+vn_tls_get(void)
+{
+ static once_flag once = ONCE_FLAG_INIT;
+ call_once(&once, vn_tls_key_create_once);
+ if (unlikely(!vn_tls_key_valid))
+ return NULL;
+
+ struct vn_tls *tls = tss_get(vn_tls_key);
+ if (likely(tls))
+ return tls;
+
+ tls = calloc(1, sizeof(*tls));
+ if (!tls)
+ return NULL;
+
+ /* initialize tls */
+ tls->async_pipeline_create = false;
+ list_inithead(&tls->tls_rings);
+
+ if (tss_set(vn_tls_key, tls) != thrd_success) {
+ free(tls);
+ return NULL;
+ }
+
+ return tls;
+}