Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf: cs-etm: Support version 0.1 of HW_ID packets

v0.1 HW_ID packets have a new field that describes which sink each CPU
writes to. Use the sink ID to link trace ID maps to each other so that
mappings are shared wherever the sink is shared.

Also update the error message to show that overlapping IDs aren't an
error in per-thread mode, just not supported. In the future we can
use the CPU ID from the AUX records, or watch for changing sink IDs on
HW_ID packets to use the correct decoders.

Reviewed-by: Mike Leach <mike.leach@linaro.org>
Signed-off-by: James Clark <james.clark@arm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: Ganapatrao Kulkarni <gankulkarni@os.amperecomputing.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20240722101202.26915-7-james.clark@linaro.org
Signed-off-by: James Clark <james.clark@linaro.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

James Clark and committed by
Arnaldo Carvalho de Melo
1506af6d 940007ce

+103 -14
+13 -4
tools/include/linux/coresight-pmu.h
··· 49 49 * Interpretation of the PERF_RECORD_AUX_OUTPUT_HW_ID payload. 50 50 * Used to associate a CPU with the CoreSight Trace ID. 51 51 * [07:00] - Trace ID - uses 8 bits to make value easy to read in file. 52 - * [59:08] - Unused (SBZ) 53 - * [63:60] - Version 52 + * [39:08] - Sink ID - as reported in /sys/bus/event_source/devices/cs_etm/sinks/ 53 + * Added in minor version 1. 54 + * [55:40] - Unused (SBZ) 55 + * [59:56] - Minor Version - previously existing fields are compatible with 56 + * all minor versions. 57 + * [63:60] - Major Version - previously existing fields mean different things 58 + * in new major versions. 54 59 */ 55 60 #define CS_AUX_HW_ID_TRACE_ID_MASK GENMASK_ULL(7, 0) 56 - #define CS_AUX_HW_ID_VERSION_MASK GENMASK_ULL(63, 60) 61 + #define CS_AUX_HW_ID_SINK_ID_MASK GENMASK_ULL(39, 8) 57 62 58 - #define CS_AUX_HW_ID_CURR_VERSION 0 63 + #define CS_AUX_HW_ID_MINOR_VERSION_MASK GENMASK_ULL(59, 56) 64 + #define CS_AUX_HW_ID_MAJOR_VERSION_MASK GENMASK_ULL(63, 60) 65 + 66 + #define CS_AUX_HW_ID_MAJOR_VERSION 0 67 + #define CS_AUX_HW_ID_MINOR_VERSION 1 59 68 60 69 #endif
+90 -10
tools/perf/util/cs-etm.c
··· 118 118 struct cs_etm_traceid_queue **traceid_queues; 119 119 /* Conversion between traceID and metadata pointers */ 120 120 struct intlist *traceid_list; 121 + /* 122 + * Same as traceid_list, but traceid_list may be a reference to another 123 + * queue's which has a matching sink ID. 124 + */ 125 + struct intlist *own_traceid_list; 126 + u32 sink_id; 121 127 }; 122 128 123 129 static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm); ··· 148 142 (queue_nr << 16 | trace_chan_id) 149 143 #define TO_QUEUE_NR(cs_queue_nr) (cs_queue_nr >> 16) 150 144 #define TO_TRACE_CHAN_ID(cs_queue_nr) (cs_queue_nr & 0x0000ffff) 145 + #define SINK_UNSET ((u32) -1) 151 146 152 147 static u32 cs_etm__get_v7_protocol_version(u32 etmidr) 153 148 { ··· 248 241 int err; 249 242 250 243 if (curr_cpu_data[CS_ETM_CPU] != cpu_metadata[CS_ETM_CPU]) { 251 - pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n"); 244 + /* 245 + * With > CORESIGHT_TRACE_IDS_MAX ETMs, overlapping IDs 246 + * are expected (but not supported) in per-thread mode, 247 + * rather than signifying an error. 248 + */ 249 + if (etmq->etm->per_thread_decoding) 250 + pr_err("CS_ETM: overlapping Trace IDs aren't currently supported in per-thread mode\n"); 251 + else 252 + pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n"); 253 + 252 254 return -EINVAL; 253 255 } 254 256 ··· 340 324 * the correct trace ID into the metadata for setting up decoders later. 341 325 */ 342 326 return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data); 327 + } 328 + 329 + static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, 330 + u64 hw_id) 331 + { 332 + struct cs_etm_queue *etmq = cs_etm__get_queue(etm, cpu); 333 + int ret; 334 + u64 *cpu_data; 335 + u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); 336 + u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); 337 + 338 + /* 339 + * Check sink id hasn't changed in per-cpu mode. In per-thread mode, 340 + * let it pass for now until an actual overlapping trace ID is hit. In 341 + * most cases IDs won't overlap even if the sink changes. 342 + */ 343 + if (!etmq->etm->per_thread_decoding && etmq->sink_id != SINK_UNSET && 344 + etmq->sink_id != sink_id) { 345 + pr_err("CS_ETM: mismatch between sink IDs\n"); 346 + return -EINVAL; 347 + } 348 + 349 + etmq->sink_id = sink_id; 350 + 351 + /* Find which other queues use this sink and link their ID maps */ 352 + for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { 353 + struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; 354 + 355 + /* Different sinks, skip */ 356 + if (other_etmq->sink_id != etmq->sink_id) 357 + continue; 358 + 359 + /* Already linked, skip */ 360 + if (other_etmq->traceid_list == etmq->traceid_list) 361 + continue; 362 + 363 + /* At the point of first linking, this one should be empty */ 364 + if (!intlist__empty(etmq->traceid_list)) { 365 + pr_err("CS_ETM: Can't link populated trace ID lists\n"); 366 + return -EINVAL; 367 + } 368 + 369 + etmq->own_traceid_list = NULL; 370 + intlist__delete(etmq->traceid_list); 371 + etmq->traceid_list = other_etmq->traceid_list; 372 + break; 373 + } 374 + 375 + cpu_data = get_cpu_data(etm, cpu); 376 + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); 377 + if (ret) 378 + return ret; 379 + 380 + ret = cs_etm__metadata_set_trace_id(trace_id, cpu_data); 381 + if (ret) 382 + return ret; 383 + 384 + return 0; 343 385 } 344 386 345 387 static int cs_etm__metadata_get_trace_id(u8 *trace_chan_id, u64 *cpu_metadata) ··· 488 414 489 415 /* extract and parse the HW ID */ 490 416 hw_id = event->aux_output_hw_id.hw_id; 491 - version = FIELD_GET(CS_AUX_HW_ID_VERSION_MASK, hw_id); 417 + version = FIELD_GET(CS_AUX_HW_ID_MAJOR_VERSION_MASK, hw_id); 492 418 493 419 /* check that we can handle this version */ 494 - if (version > CS_AUX_HW_ID_CURR_VERSION) { 420 + if (version > CS_AUX_HW_ID_MAJOR_VERSION) { 495 421 pr_err("CS ETM Trace: PERF_RECORD_AUX_OUTPUT_HW_ID version %d not supported. Please update Perf.\n", 496 422 version); 497 423 return -EINVAL; ··· 516 442 return -EINVAL; 517 443 } 518 444 519 - return cs_etm__process_trace_id_v0(etm, cpu, hw_id); 445 + if (FIELD_GET(CS_AUX_HW_ID_MINOR_VERSION_MASK, hw_id) == 0) 446 + return cs_etm__process_trace_id_v0(etm, cpu, hw_id); 447 + 448 + return cs_etm__process_trace_id_v0_1(etm, cpu, hw_id); 520 449 } 521 450 522 451 void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, ··· 959 882 cs_etm_decoder__free(etmq->decoder); 960 883 cs_etm__free_traceid_queues(etmq); 961 884 962 - /* First remove all traceID/metadata nodes for the RB tree */ 963 - intlist__for_each_entry_safe(inode, tmp, etmq->traceid_list) 964 - intlist__remove(etmq->traceid_list, inode); 885 + if (etmq->own_traceid_list) { 886 + /* First remove all traceID/metadata nodes for the RB tree */ 887 + intlist__for_each_entry_safe(inode, tmp, etmq->own_traceid_list) 888 + intlist__remove(etmq->own_traceid_list, inode); 965 889 966 - /* Then the RB tree itself */ 967 - intlist__delete(etmq->traceid_list); 890 + /* Then the RB tree itself */ 891 + intlist__delete(etmq->own_traceid_list); 892 + } 968 893 969 894 free(etmq); 970 895 } ··· 1160 1081 * has to be made for each packet that gets decoded, optimizing access 1161 1082 * in anything other than a sequential array is worth doing. 1162 1083 */ 1163 - etmq->traceid_list = intlist__new(NULL); 1084 + etmq->traceid_list = etmq->own_traceid_list = intlist__new(NULL); 1164 1085 if (!etmq->traceid_list) 1165 1086 goto out_free; 1166 1087 ··· 1192 1113 etmq->queue_nr = queue_nr; 1193 1114 queue->cpu = queue_nr; /* Placeholder, may be reset to -1 in per-thread mode */ 1194 1115 etmq->offset = 0; 1116 + etmq->sink_id = SINK_UNSET; 1195 1117 1196 1118 return 0; 1197 1119 }