util/u_trace: refcount payloads

When cloning a chunk of tracepoints, we cannot just copy the elements
of the traces[] array. We also need the payloads associated with
those.

This change introduces a new u_trace_payloaf_buf object that is
refcounted so that we can easily import traces[] elements and their
payloads from one utrace to another.

v2: use u_vector (Danylo)

v3: Delete outdate comment (Danylo)
    Fix assert (Danylo)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Fixes: 0565c993f9 ("u_trace: helpers for tracing tiling GPUs and re-usable VK cmdbuffers")
Reviewed-by: Danylo Piliaiev <dpiliaiev@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/13899>
This commit is contained in:
Lionel Landwerlin 2021-11-21 01:06:09 +02:00 committed by Marge Bot
parent 87888c0b3f
commit 14e45cb21e
2 changed files with 101 additions and 29 deletions

View file

@ -24,16 +24,17 @@
#include <inttypes.h>
#include "util/list.h"
#include "util/ralloc.h"
#include "util/u_debug.h"
#include "util/u_inlines.h"
#include "util/u_fifo.h"
#include "util/u_vector.h"
#include "u_trace.h"
#define __NEEDS_TRACE_PRIV
#include "u_trace_priv.h"
#define PAYLOAD_BUFFER_SIZE 0x100
#define TIMESTAMP_BUF_SIZE 0x1000
#define TRACES_PER_CHUNK (TIMESTAMP_BUF_SIZE / sizeof(uint64_t))
@ -49,6 +50,14 @@ int ut_perfetto_enabled;
struct list_head ctx_list = { &ctx_list, &ctx_list };
#endif
struct u_trace_payload_buf {
uint32_t refcount;
uint8_t *buf;
uint8_t *next;
uint8_t *end;
};
struct u_trace_event {
const struct u_tracepoint *tp;
const void *payload;
@ -76,12 +85,12 @@ struct u_trace_chunk {
*/
void *timestamps;
/**
* For trace payload, we sub-allocate from ralloc'd buffers which
* hang off of the chunk's ralloc context, so they are automatically
* free'd when the chunk is free'd
/* Array of u_trace_payload_buf referenced by traces[] elements.
*/
uint8_t *payload_buf, *payload_end;
struct u_vector payloads;
/* Current payload buffer being written. */
struct u_trace_payload_buf *payload;
struct util_queue_fence fence;
@ -97,6 +106,35 @@ struct u_trace_chunk {
bool free_flush_data;
};
static struct u_trace_payload_buf *
u_trace_payload_buf_create(void)
{
struct u_trace_payload_buf *payload =
malloc(sizeof(*payload) + PAYLOAD_BUFFER_SIZE);
p_atomic_set(&payload->refcount, 1);
payload->buf = (uint8_t *) (payload + 1);
payload->end = payload->buf + PAYLOAD_BUFFER_SIZE;
payload->next = payload->buf;
return payload;
}
static struct u_trace_payload_buf *
u_trace_payload_buf_ref(struct u_trace_payload_buf *payload)
{
p_atomic_inc(&payload->refcount);
return payload;
}
static void
u_trace_payload_buf_unref(struct u_trace_payload_buf *payload)
{
if (p_atomic_dec_zero(&payload->refcount))
free(payload);
}
static void
free_chunk(void *ptr)
{
@ -104,7 +142,14 @@ free_chunk(void *ptr)
chunk->utctx->delete_timestamp_buffer(chunk->utctx, chunk->timestamps);
/* Unref payloads attached to this chunk. */
struct u_trace_payload_buf **payload;
u_vector_foreach(payload, &chunk->payloads)
u_trace_payload_buf_unref(*payload);
u_vector_finish(&chunk->payloads);
list_del(&chunk->node);
free(chunk);
}
static void
@ -113,21 +158,41 @@ free_chunks(struct list_head *chunks)
while (!list_is_empty(chunks)) {
struct u_trace_chunk *chunk = list_first_entry(chunks,
struct u_trace_chunk, node);
ralloc_free(chunk);
free_chunk(chunk);
}
}
static struct u_trace_chunk *
get_chunk(struct u_trace *ut)
get_chunk(struct u_trace *ut, size_t payload_size)
{
struct u_trace_chunk *chunk;
assert(payload_size <= PAYLOAD_BUFFER_SIZE);
/* do we currently have a non-full chunk to append msgs to? */
if (!list_is_empty(&ut->trace_chunks)) {
chunk = list_last_entry(&ut->trace_chunks,
struct u_trace_chunk, node);
if (chunk->num_traces < TRACES_PER_CHUNK)
return chunk;
/* Can we store a new trace in the chunk? */
if (chunk->num_traces < TRACES_PER_CHUNK) {
/* If no payload required, nothing else to check. */
if (payload_size <= 0)
return chunk;
/* If the payload buffer has space for the payload, we're good.
*/
if (chunk->payload &&
(chunk->payload->end - chunk->payload->next) >= payload_size)
return chunk;
/* If we don't have enough space in the payload buffer, can we
* allocate a new one?
*/
struct u_trace_payload_buf **buf = u_vector_add(&chunk->payloads);
*buf = u_trace_payload_buf_create();
chunk->payload = *buf;
return chunk;
}
/* we need to expand to add another chunk to the batch, so
* the current one is no longer the last one of the batch:
*/
@ -135,12 +200,17 @@ get_chunk(struct u_trace *ut)
}
/* .. if not, then create a new one: */
chunk = rzalloc_size(NULL, sizeof(*chunk));
ralloc_set_destructor(chunk, free_chunk);
chunk = calloc(1, sizeof(*chunk));
chunk->utctx = ut->utctx;
chunk->timestamps = ut->utctx->create_timestamp_buffer(ut->utctx, TIMESTAMP_BUF_SIZE);
chunk->last = true;
u_vector_init(&chunk->payloads, 4, sizeof(struct u_trace_payload_buf *));
if (payload_size > 0) {
struct u_trace_payload_buf **buf = u_vector_add(&chunk->payloads);
*buf = u_trace_payload_buf_create();
chunk->payload = *buf;
}
list_addtail(&chunk->node, &ut->trace_chunks);
@ -319,7 +389,7 @@ process_chunk(void *job, void *gdata, int thread_index)
static void
cleanup_chunk(void *job, void *gdata, int thread_index)
{
ralloc_free(job);
free_chunk(job);
}
void
@ -417,7 +487,7 @@ u_trace_clone_append(struct u_trace_iterator begin_it,
uint32_t from_idx = begin_it.event_idx;
while (from_chunk != end_it.chunk || from_idx != end_it.event_idx) {
struct u_trace_chunk *to_chunk = get_chunk(into);
struct u_trace_chunk *to_chunk = get_chunk(into, 0 /* payload_size */);
unsigned to_copy = MIN2(TRACES_PER_CHUNK - to_chunk->num_traces,
from_chunk->num_traces - from_idx);
@ -433,6 +503,17 @@ u_trace_clone_append(struct u_trace_iterator begin_it,
&from_chunk->traces[from_idx],
to_copy * sizeof(struct u_trace_event));
/* Take a refcount on payloads from from_chunk if needed. */
if (begin_it.ut != into) {
struct u_trace_payload_buf **in_payload;
u_vector_foreach(in_payload, &from_chunk->payloads) {
struct u_trace_payload_buf **out_payload =
u_vector_add(&to_chunk->payloads);
*out_payload = u_trace_payload_buf_ref(*in_payload);
}
}
to_chunk->num_traces += to_copy;
from_idx += to_copy;
@ -473,22 +554,16 @@ u_trace_disable_event_range(struct u_trace_iterator begin_it,
void *
u_trace_append(struct u_trace *ut, void *cs, const struct u_tracepoint *tp)
{
struct u_trace_chunk *chunk = get_chunk(ut);
struct u_trace_chunk *chunk = get_chunk(ut, tp->payload_sz);
assert(tp->payload_sz == ALIGN_NPOT(tp->payload_sz, 8));
if (unlikely((chunk->payload_buf + tp->payload_sz) > chunk->payload_end)) {
const unsigned payload_chunk_sz = 0x100; /* TODO arbitrary size? */
assert(tp->payload_sz < payload_chunk_sz);
chunk->payload_buf = ralloc_size(chunk, payload_chunk_sz);
chunk->payload_end = chunk->payload_buf + payload_chunk_sz;
}
/* sub-allocate storage for trace payload: */
void *payload = chunk->payload_buf;
chunk->payload_buf += tp->payload_sz;
void *payload = NULL;
if (tp->payload_sz > 0) {
payload = chunk->payload->next;
chunk->payload->next += tp->payload_sz;
}
/* record a timestamp for the trace: */
ut->utctx->record_timestamp(ut, cs, chunk->timestamps, chunk->num_traces);

View file

@ -235,9 +235,6 @@ typedef void (*u_trace_copy_ts_buffer)(struct u_trace_context *utctx,
* Provides callback for driver to copy timestamps on GPU from
* one buffer to another.
*
* The payload is shared and remains owned by the original u_trace
* if tracepoints are being copied between different u_trace!
*
* It allows:
* - Tracing re-usable command buffer in Vulkan, by copying tracepoints
* each time it is submitted.