mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
perf cs-etm: Synthesize missing HW_ID mappings for raw trace
The CoreSight driver emits PERF_RECORD_AUX_OUTPUT_HW_ID via perf_report_aux_output_id(). If the perf ring buffer is full, perf_report_aux_output_id() can fail to emit the record and return silently. When at least one HW_ID record is present in the file, perf skips the legacy metadata-only trace ID mapping path. Therefore any raw AUX queue whose HW_ID record was dropped can be left with an empty traceid_list, and cs_etm__create_queue_decoders() will not create a decoder for that queue. Fix it by ignoring all HW_IDs for unformatted queues and always create one decoder per queue unconditionally. Trace ID is unused so we can hard code it to 1 (0 is a special value used in 'pending_timestamp_chan_id' to indicate not pending). We also need put the same trace ID in the CPU metadata because the relationship between CPU metadata and traceid_list is bidirectional. Moving cs_etm__metadata_set_trace_id() into the mapping function solves this and simplifies existing code. cs_etm__insert_trace_id_node() now does two things, so rename it to a more generic cs_etm__map_trace_id(). Signed-off-by: Leo Yan <leo.yan@arm.com> Signed-off-by: James Clark <james.clark@linaro.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
18eaa04d94
commit
e4c26138ec
|
|
@ -243,8 +243,12 @@ enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq)
|
|||
return etmq->etm->pid_fmt;
|
||||
}
|
||||
|
||||
static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq,
|
||||
u8 trace_chan_id, u64 *cpu_metadata)
|
||||
/*
|
||||
* Add a traceid_list entry that maps a trace ID to a CPU metadata for this etmq
|
||||
* and update the trace ID in the cpu_metadata.
|
||||
*/
|
||||
static int cs_etm__map_trace_id(struct cs_etm_queue *etmq, u8 trace_chan_id,
|
||||
u64 *cpu_metadata)
|
||||
{
|
||||
/* Get an RB node for this CPU */
|
||||
struct int_node *inode = intlist__findnew(etmq->traceid_list, trace_chan_id);
|
||||
|
|
@ -289,8 +293,7 @@ static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq,
|
|||
|
||||
/* Not one we've seen before, associate the traceID with the metadata pointer */
|
||||
inode->priv = cpu_metadata;
|
||||
|
||||
return 0;
|
||||
return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_metadata);
|
||||
}
|
||||
|
||||
static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int cpu)
|
||||
|
|
@ -314,12 +317,11 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
|
|||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If the queue is unformatted then only save one mapping in the
|
||||
* queue associated with that CPU so only one decoder is made.
|
||||
* Ignore HW_IDs for unformatted, a decoder will be made
|
||||
* unconditionally later.
|
||||
*/
|
||||
if (etmq->format == UNFORMATTED)
|
||||
return cs_etm__insert_trace_id_node(etmq, trace_chan_id,
|
||||
cpu_metadata);
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Otherwise, version 0 trace IDs are global so save them into every
|
||||
|
|
@ -332,8 +334,7 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
|
|||
if (!etmq || etmq->format == UNFORMATTED)
|
||||
continue;
|
||||
|
||||
ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id,
|
||||
cpu_metadata);
|
||||
ret = cs_etm__map_trace_id(etmq, trace_chan_id, cpu_metadata);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -344,7 +345,6 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
|
|||
static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu,
|
||||
u64 hw_id)
|
||||
{
|
||||
int err;
|
||||
u64 *cpu_data;
|
||||
u8 trace_chan_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
|
||||
|
||||
|
|
@ -352,22 +352,13 @@ static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu,
|
|||
if (cpu_data == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
err = cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* if we are picking up the association from the packet, need to plug
|
||||
* the correct trace ID into the metadata for setting up decoders later.
|
||||
*/
|
||||
return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data);
|
||||
return cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data);
|
||||
}
|
||||
|
||||
static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
|
||||
u64 hw_id)
|
||||
{
|
||||
struct cs_etm_queue *etmq = cs_etm__get_queue(etm, cpu);
|
||||
int ret;
|
||||
u64 *cpu_data;
|
||||
u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id);
|
||||
u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
|
||||
|
|
@ -375,6 +366,13 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
|
|||
if (!etmq)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Ignore HW_IDs for unformatted, a decoder will be made
|
||||
* unconditionally later.
|
||||
*/
|
||||
if (etmq->format == UNFORMATTED)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check sink id hasn't changed in per-cpu mode. In per-thread mode,
|
||||
* let it pass for now until an actual overlapping trace ID is hit. In
|
||||
|
|
@ -419,13 +417,41 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
|
|||
if (!cpu_data)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
return cs_etm__map_trace_id(etmq, trace_id, cpu_data);
|
||||
}
|
||||
|
||||
ret = cs_etm__metadata_set_trace_id(trace_id, cpu_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
static int cs_etm__map_unformatted_trace_ids(struct cs_etm_auxtrace *etm)
|
||||
{
|
||||
struct auxtrace_queues *queues = &etm->queues;
|
||||
|
||||
for (unsigned int i = 0; i < queues->nr_queues; i++) {
|
||||
struct auxtrace_queue *queue = &queues->queue_array[i];
|
||||
struct cs_etm_queue *etmq = queue->priv;
|
||||
u64 *cpu_data;
|
||||
int ret;
|
||||
|
||||
if (list_empty(&queue->head) || !etmq || etmq->format != UNFORMATTED)
|
||||
continue;
|
||||
|
||||
if (!intlist__empty(etmq->traceid_list)) {
|
||||
pr_err("CS_ETM: Unformatted queue should't have trace ID\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (etm->per_thread_decoding)
|
||||
cpu_data = etm->metadata[0];
|
||||
else
|
||||
cpu_data = get_cpu_data(etm, queue->cpu);
|
||||
|
||||
if (!cpu_data) {
|
||||
pr_err("CS_ETM: Unformatted queue missing CPU metadata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = cs_etm__map_trace_id(etmq, 1, cpu_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3757,6 +3783,11 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
|
|||
goto err_free_queues;
|
||||
}
|
||||
|
||||
/* Create IDs for TRBE sinks so that a decoder is made for each one. */
|
||||
err = cs_etm__map_unformatted_trace_ids(etm);
|
||||
if (err)
|
||||
goto err_free_queues;
|
||||
|
||||
err = cs_etm__create_decoders(etm);
|
||||
if (err)
|
||||
goto err_free_queues;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user