Skip to content

Commit 5cb75f1

Browse files
Suzuki K Poulosemathieupoirier
authored andcommitted
coresight: trbe: Workaround TRBE errata overwrite in FILL mode
ARM Neoverse-N2 (#2139208) and Cortex-A710(##2119858) suffers from an erratum, which when triggered, might cause the TRBE to overwrite the trace data already collected in FILL mode, in the event of a WRAP. i.e, the TRBE doesn't stop writing the data, instead wraps to the base and could write upto 3 cache line size worth trace. Thus, this could corrupt the trace at the "BASE" pointer. The workaround is to program the write pointer 256bytes from the base, such that if the erratum is triggered, it doesn't overwrite the trace data that was captured. This skipped region could be padded with ignore packets at the end of the session, so that the decoder sees a continuous buffer with some padding at the beginning. The trace data written at the base is considered lost as the limit could have been in the middle of the perf ring buffer, and jumping to the "base" is not acceptable. We set the flags already to indicate that some amount of trace was lost during the FILL event IRQ. So this is fine. One important change with the work around is, we program the TRBBASER_EL1 to current page where we are allowed to write. Otherwise, it could overwrite a region that may be consumed by the perf. Towards this, we always make sure that the "handle->head" and thus the trbe_write is PAGE_SIZE aligned, so that we can set the BASE to the PAGE base and move the TRBPTR to the 256bytes offset. Cc: Mike Leach <[email protected]> Cc: Mathieu Poirier <[email protected]> Cc: Anshuman Khandual <[email protected]> Cc: Leo Yan <[email protected]> Reviewed-by: Anshuman Khandual <[email protected]> Signed-off-by: Suzuki K Poulose <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mathieu Poirier <[email protected]>
1 parent 8a10651 commit 5cb75f1

File tree

1 file changed

+157
-11
lines changed

1 file changed

+157
-11
lines changed

drivers/hwtracing/coresight/coresight-trbe.c

Lines changed: 157 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,22 @@ struct trbe_buf {
8989
* - Not duplicating the detection logic
9090
* - Streamlined detection of erratum across the system
9191
*/
92+
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0
9293

9394
static int trbe_errata_cpucaps[] = {
95+
[TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE,
9496
-1, /* Sentinel, must be the last entry */
9597
};
9698

9799
/* The total number of listed errata in trbe_errata_cpucaps */
98100
#define TRBE_ERRATA_MAX (ARRAY_SIZE(trbe_errata_cpucaps) - 1)
99101

102+
/*
103+
* Safe limit for the number of bytes that may be overwritten
104+
* when ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE is triggered.
105+
*/
106+
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
107+
100108
/*
101109
* struct trbe_cpudata: TRBE instance specific data
102110
* @trbe_flag - TRBE dirty/access flag support
@@ -147,6 +155,11 @@ static inline bool trbe_has_erratum(struct trbe_cpudata *cpudata, int i)
147155
return (i < TRBE_ERRATA_MAX) && test_bit(i, cpudata->errata);
148156
}
149157

158+
static inline bool trbe_may_overwrite_in_fill_mode(struct trbe_cpudata *cpudata)
159+
{
160+
return trbe_has_erratum(cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE);
161+
}
162+
150163
static int trbe_alloc_node(struct perf_event *event)
151164
{
152165
if (event->cpu == -1)
@@ -550,10 +563,13 @@ static void trbe_enable_hw(struct trbe_buf *buf)
550563
set_trbe_limit_pointer_enabled(buf->trbe_limit);
551564
}
552565

553-
static enum trbe_fault_action trbe_get_fault_act(u64 trbsr)
566+
static enum trbe_fault_action trbe_get_fault_act(struct perf_output_handle *handle,
567+
u64 trbsr)
554568
{
555569
int ec = get_trbe_ec(trbsr);
556570
int bsc = get_trbe_bsc(trbsr);
571+
struct trbe_buf *buf = etm_perf_sink_config(handle);
572+
struct trbe_cpudata *cpudata = buf->cpudata;
557573

558574
WARN_ON(is_trbe_running(trbsr));
559575
if (is_trbe_trg(trbsr) || is_trbe_abort(trbsr))
@@ -562,10 +578,16 @@ static enum trbe_fault_action trbe_get_fault_act(u64 trbsr)
562578
if ((ec == TRBE_EC_STAGE1_ABORT) || (ec == TRBE_EC_STAGE2_ABORT))
563579
return TRBE_FAULT_ACT_FATAL;
564580

565-
if (is_trbe_wrap(trbsr) && (ec == TRBE_EC_OTHERS) && (bsc == TRBE_BSC_FILLED)) {
566-
if (get_trbe_write_pointer() == get_trbe_base_pointer())
567-
return TRBE_FAULT_ACT_WRAP;
568-
}
581+
/*
582+
* If the trbe is affected by TRBE_WORKAROUND_OVERWRITE_FILL_MODE,
583+
* it might write data after a WRAP event in the fill mode.
584+
* Thus the check TRBPTR == TRBBASER will not be honored.
585+
*/
586+
if ((is_trbe_wrap(trbsr) && (ec == TRBE_EC_OTHERS) && (bsc == TRBE_BSC_FILLED)) &&
587+
(trbe_may_overwrite_in_fill_mode(cpudata) ||
588+
get_trbe_write_pointer() == get_trbe_base_pointer()))
589+
return TRBE_FAULT_ACT_WRAP;
590+
569591
return TRBE_FAULT_ACT_SPURIOUS;
570592
}
571593

@@ -574,6 +596,8 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle,
574596
{
575597
u64 write;
576598
u64 start_off, end_off;
599+
u64 size;
600+
u64 overwrite_skip = TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES;
577601

578602
/*
579603
* If the TRBE has wrapped around the write pointer has
@@ -594,7 +618,18 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle,
594618

595619
if (WARN_ON_ONCE(end_off < start_off))
596620
return 0;
597-
return (end_off - start_off);
621+
622+
size = end_off - start_off;
623+
/*
624+
* If the TRBE is affected by the following erratum, we must fill
625+
* the space we skipped with IGNORE packets. And we are always
626+
* guaranteed to have at least a PAGE_SIZE space in the buffer.
627+
*/
628+
if (trbe_has_erratum(buf->cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE) &&
629+
!WARN_ON(size < overwrite_skip))
630+
__trbe_pad_buf(buf, start_off, overwrite_skip);
631+
632+
return size;
598633
}
599634

600635
static void *arm_trbe_alloc_buffer(struct coresight_device *csdev,
@@ -713,7 +748,7 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev,
713748
clr_trbe_irq();
714749
isb();
715750

716-
act = trbe_get_fault_act(status);
751+
act = trbe_get_fault_act(handle, status);
717752
/*
718753
* If this was not due to a WRAP event, we have some
719754
* errors and as such buffer is empty.
@@ -737,21 +772,117 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev,
737772
return size;
738773
}
739774

775+
776+
static int trbe_apply_work_around_before_enable(struct trbe_buf *buf)
777+
{
778+
/*
779+
* TRBE_WORKAROUND_OVERWRITE_FILL_MODE causes the TRBE to overwrite a few cache
780+
* line size from the "TRBBASER_EL1" in the event of a "FILL".
781+
* Thus, we could loose some amount of the trace at the base.
782+
*
783+
* Before Fix:
784+
*
785+
* normal-BASE head (normal-TRBPTR) tail (normal-LIMIT)
786+
* | \/ /
787+
* -------------------------------------------------------------
788+
* | Pg0 | Pg1 | | | PgN |
789+
* -------------------------------------------------------------
790+
*
791+
* In the normal course of action, we would set the TRBBASER to the
792+
* beginning of the ring-buffer (normal-BASE). But with the erratum,
793+
* the TRBE could overwrite the contents at the "normal-BASE", after
794+
* hitting the "normal-LIMIT", since it doesn't stop as expected. And
795+
* this is wrong. This could result in overwriting trace collected in
796+
* one of the previous runs, being consumed by the user. So we must
797+
* always make sure that the TRBBASER is within the region
798+
* [head, head+size]. Note that TRBBASER must be PAGE aligned,
799+
*
800+
* After moving the BASE:
801+
*
802+
* normal-BASE head (normal-TRBPTR) tail (normal-LIMIT)
803+
* | \/ /
804+
* -------------------------------------------------------------
805+
* | | |xyzdef. |.. tuvw| |
806+
* -------------------------------------------------------------
807+
* /
808+
* New-BASER
809+
*
810+
* Also, we would set the TRBPTR to head (after adjusting for
811+
* alignment) at normal-PTR. This would mean that the last few bytes
812+
* of the trace (say, "xyz") might overwrite the first few bytes of
813+
* trace written ("abc"). More importantly they will appear in what
814+
* userspace sees as the beginning of the trace, which is wrong. We may
815+
* not always have space to move the latest trace "xyz" to the correct
816+
* order as it must appear beyond the LIMIT. (i.e, [head..head+size]).
817+
* Thus it is easier to ignore those bytes than to complicate the
818+
* driver to move it, assuming that the erratum was triggered and
819+
* doing additional checks to see if there is indeed allowed space at
820+
* TRBLIMITR.LIMIT.
821+
*
822+
* Thus the full workaround will move the BASE and the PTR and would
823+
* look like (after padding at the skipped bytes at the end of
824+
* session) :
825+
*
826+
* normal-BASE head (normal-TRBPTR) tail (normal-LIMIT)
827+
* | \/ /
828+
* -------------------------------------------------------------
829+
* | | |///abc.. |.. rst| |
830+
* -------------------------------------------------------------
831+
* / |
832+
* New-BASER New-TRBPTR
833+
*
834+
* To summarize, with the work around:
835+
*
836+
* - We always align the offset for the next session to PAGE_SIZE
837+
* (This is to ensure we can program the TRBBASER to this offset
838+
* within the region [head...head+size]).
839+
*
840+
* - At TRBE enable:
841+
* - Set the TRBBASER to the page aligned offset of the current
842+
* proposed write offset. (which is guaranteed to be aligned
843+
* as above)
844+
* - Move the TRBPTR to skip first 256bytes (that might be
845+
* overwritten with the erratum). This ensures that the trace
846+
* generated in the session is not re-written.
847+
*
848+
* - At trace collection:
849+
* - Pad the 256bytes skipped above again with IGNORE packets.
850+
*/
851+
if (trbe_has_erratum(buf->cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE)) {
852+
if (WARN_ON(!IS_ALIGNED(buf->trbe_write, PAGE_SIZE)))
853+
return -EINVAL;
854+
buf->trbe_hw_base = buf->trbe_write;
855+
buf->trbe_write += TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES;
856+
}
857+
858+
return 0;
859+
}
860+
740861
static int __arm_trbe_enable(struct trbe_buf *buf,
741862
struct perf_output_handle *handle)
742863
{
864+
int ret = 0;
865+
743866
perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW);
744867
buf->trbe_limit = compute_trbe_buffer_limit(handle);
745868
buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf);
746869
if (buf->trbe_limit == buf->trbe_base) {
747-
trbe_stop_and_truncate_event(handle);
748-
return -ENOSPC;
870+
ret = -ENOSPC;
871+
goto err;
749872
}
750873
/* Set the base of the TRBE to the buffer base */
751874
buf->trbe_hw_base = buf->trbe_base;
875+
876+
ret = trbe_apply_work_around_before_enable(buf);
877+
if (ret)
878+
goto err;
879+
752880
*this_cpu_ptr(buf->cpudata->drvdata->handle) = handle;
753881
trbe_enable_hw(buf);
754882
return 0;
883+
err:
884+
trbe_stop_and_truncate_event(handle);
885+
return ret;
755886
}
756887

757888
static int arm_trbe_enable(struct coresight_device *csdev, u32 mode, void *data)
@@ -891,7 +1022,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
8911022
if (!is_perf_trbe(handle))
8921023
return IRQ_NONE;
8931024

894-
act = trbe_get_fault_act(status);
1025+
act = trbe_get_fault_act(handle, status);
8951026
switch (act) {
8961027
case TRBE_FAULT_ACT_WRAP:
8971028
truncated = !!trbe_handle_overflow(handle);
@@ -1043,7 +1174,22 @@ static void arm_trbe_probe_cpu(void *info)
10431174
*/
10441175
trbe_check_errata(cpudata);
10451176

1046-
cpudata->trbe_align = cpudata->trbe_hw_align;
1177+
/*
1178+
* If the TRBE is affected by erratum TRBE_WORKAROUND_OVERWRITE_FILL_MODE,
1179+
* we must always program the TBRPTR_EL1, 256bytes from a page
1180+
* boundary, with TRBBASER_EL1 set to the page, to prevent
1181+
* TRBE over-writing 256bytes at TRBBASER_EL1 on FILL event.
1182+
*
1183+
* Thus make sure we always align our write pointer to a PAGE_SIZE,
1184+
* which also guarantees that we have at least a PAGE_SIZE space in
1185+
* the buffer (TRBLIMITR is PAGE aligned) and thus we can skip
1186+
* the required bytes at the base.
1187+
*/
1188+
if (trbe_may_overwrite_in_fill_mode(cpudata))
1189+
cpudata->trbe_align = PAGE_SIZE;
1190+
else
1191+
cpudata->trbe_align = cpudata->trbe_hw_align;
1192+
10471193
cpudata->trbe_flag = get_trbe_flag_update(trbidr);
10481194
cpudata->cpu = cpu;
10491195
cpudata->drvdata = drvdata;

0 commit comments

Comments
 (0)