Add tests to verify the calculation of the limit and trigger count.
Because trigger mode can be disabled, provide two test suites: one with trigger mode enabled and one with it disabled.
The cpudata structure is initialized by the test stub, so move its definition into the header for including.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/Kconfig | 9 + drivers/hwtracing/coresight/Makefile | 1 + .../coresight/coresight-trbe-kunit-tests.c | 536 +++++++++++++++++++++ drivers/hwtracing/coresight/coresight-trbe.c | 112 +---- drivers/hwtracing/coresight/coresight-trbe.h | 89 ++++ 5 files changed, 660 insertions(+), 87 deletions(-)
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 6a4239ebb582e95f0ebe8e9c8738a726f27f60a1..f5758563c0090141cdca67f16e7b7b32e7c75bb8 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -214,6 +214,15 @@ config CORESIGHT_TRBE To compile this driver as a module, choose M here: the module will be called coresight-trbe.
+config CORESIGHT_TRBE_KUNIT_TESTS + tristate "Enable Coresight TRBE unit tests" + depends on KUNIT + depends on CORESIGHT_TRBE + default KUNIT_ALL_TESTS + help + Enable Coresight TRBE unit tests. Only useful for development and not + intended for production. + config ULTRASOC_SMB tristate "Ultrasoc system memory buffer drivers" depends on ACPI || COMPILE_TEST diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index ab16d06783a572ea1308dfb3a30c96df9e5ffdb7..f8961f6883d167bc2c4bca8008eceb08c3c3a0e9 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o coresight-ctcu-y := coresight-ctcu-core.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o +obj-$(CONFIG_CORESIGHT_TRBE_KUNIT_TESTS) += coresight-trbe-kunit-tests.o diff --git a/drivers/hwtracing/coresight/coresight-trbe-kunit-tests.c b/drivers/hwtracing/coresight/coresight-trbe-kunit-tests.c new file mode 100644 index 0000000000000000000000000000000000000000..836f76dce155d533f9076e85dc97ba25221b7bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-trbe-kunit-tests.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <kunit/test.h> +#include <kunit/device.h> +#include <linux/coresight.h> + +#include "coresight-priv.h" +#include "coresight-trbe.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +static void test_compute_offset(struct kunit *test) +{ + struct perf_output_handle handle = { 0 }; + struct trbe_buf buf = { 0 }; + struct trbe_cpudata cpudata = { .trbe_align = PAGE_SIZE }; + unsigned long limit; + + if (!static_branch_unlikely(&trbe_trigger_mode_bypass)) + return; + + cpudata.trbe_hw_align = 1; + + buf.nr_pages = SZ_1M / SZ_4K; + buf.cpudata = &cpudata; + + handle.rb = (void *)&buf; + + /* + * ### : Free space, $$$ : Filled space + * + * |################|################| + * `head `wakeup + * `tail `limit + */ + handle.head = 0; + handle.size = SZ_1M; + handle.wakeup = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2); + + /* + * |################|################| + * `head `wakeup `tail + * `limit + */ + handle.head = 0; + handle.size = SZ_1M - 1; + handle.wakeup = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2); + + /* + * |#################################| + * `head `tail + * `wakeup `limit + */ + handle.head = 0; + handle.size = SZ_1M - 1; + handle.wakeup = 0; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, 0); + + /* + * |#################################| + * `head `tail + * `wakeup + * `limit + */ + handle.head = 0; + handle.size = SZ_1M - 1; + handle.wakeup = SZ_1M; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + + /* + * |$$$$$$$$$$$$$$$$|########|#######| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 2; + handle.size = SZ_1M / 2 - 1; + handle.wakeup = SZ_1M * 3 / 4; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4); + + /* + * |$$$$$$$$|$$$$$$$|################| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 2; + handle.size = SZ_1M / 2 - 1; + handle.wakeup = SZ_1M * 1 / 4; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + + /* + * |$$$$$$$$$$$$$$$$|################| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 2; + handle.size = SZ_1M / 2 - 1; + handle.wakeup = SZ_1M - 1; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + + /* + * |#########|$$$$$$$$$$|########|###| + * `tail `head `wakeup + * `limit + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = handle.head + SZ_1M / 8; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 7 / 8); + + /* + * |####|####|$$$$$$$$$$|############| + * `tail `head + * `wakeup + * `limit + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M + SZ_1M / 8; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + + /* + * |#######|########|$$$$$$$$$$$$$$$$| + * `head `wakeup `>tail + * `limit + */ + handle.head = SZ_1M; + handle.wakeup = SZ_1M + SZ_1M / 8; + handle.size = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M / 8); + + /* + * |#######|$$$$$$$$$$$$$$$$$|#######| + * `tail `head + * `wakeup + * `limit + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M + SZ_1M / 4; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + + /* + * |#######|$$$$$$$$|$$$$$$$$|#######| + * `tail `wakeup `head + * `limit + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M + SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + + /* + * |$$$$$$$|########|########|$$$$$$$| + * `head `wakeup `tail + * `limit + */ + handle.head = SZ_1M / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2); + + /* + * |$$$$$$$|#################|$$$$$$$| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M * 3 / 4; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4); + + /* + * |$$$$$$$|#################|$$$$$$$| + * `wakeup `head `tail + * `limit + */ + handle.head = SZ_1M / 4; + handle.size = SZ_1M / 2; + handle.wakeup = 0; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4); + + /* + * |$$$$$$|$$$$$$$$$$$$$$$$$$$$$$$$$$| + * `head + * `tail + */ + handle.head = SZ_1M / 4; + handle.size = 0; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, 0); + + /* + * |$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$|#$| + * `head + * `tail + */ + handle.head = SZ_1M - SZ_1K * 2; + handle.size = SZ_1K; + handle.wakeup = 0; + + limit = __trbe_normal_offset(&handle); + KUNIT_ASSERT_EQ(test, limit, 0); +} + +static void test_compute_offset_and_counter(struct kunit *test) +{ + struct perf_output_handle handle = { 0 }; + struct trbe_buf buf = { 0 }; + struct trbe_cpudata cpudata = { .trbe_align = PAGE_SIZE }; + unsigned long limit; + u64 count; + + if (static_branch_unlikely(&trbe_trigger_mode_bypass)) + return; + + cpudata.trbe_hw_align = 1; + + buf.nr_pages = SZ_1M / SZ_4K; + buf.cpudata = &cpudata; + + handle.rb = (void *)&buf; + + /* + * ### : Free space, $$$ : Filled space + * + * |################|################| + * `head `wakeup `limit + * `tail + * `----- count ----' + */ + handle.head = 0; + handle.size = SZ_1M; + handle.wakeup = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 2); + + /* + * |################|################| + * `head `wakeup `tail + * `limit + * `----- count ----' + */ + handle.head = 0; + handle.size = SZ_1M - 1; + handle.wakeup = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 2); + + /* + * |#################################| + * `head `tail + * `wakeup `limit + */ + handle.head = 0; + handle.size = SZ_1M - 1; + handle.wakeup = 0; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + KUNIT_ASSERT_EQ(test, count, 0); + + /* + * |#################################| + * `head `tail + * `wakeup + * `limit + */ + handle.head = 0; + handle.size = SZ_1M - 1; + handle.wakeup = SZ_1M; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + KUNIT_ASSERT_EQ(test, count, 0); + + /* + * |$$$$$$$$$$$$$$$$|########|#######| + * `head `tail + * `wakeup + * `limit + * [ count ] + */ + handle.head = SZ_1M / 2; + handle.size = SZ_1M / 2 - 1; + handle.wakeup = SZ_1M * 3 / 4; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 4); + + /* + * |$$$$$$$$|$$$$$$$|################| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 2; + handle.size = SZ_1M / 2 - 1; + handle.wakeup = SZ_1M * 1 / 4; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + KUNIT_ASSERT_EQ(test, count, 0); + + /* + * |$$$$$$$$$$$$$$$$|################| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 2; + handle.size = SZ_1M / 2 - 1; + handle.wakeup = SZ_1M - 1; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K); + KUNIT_ASSERT_EQ(test, count, 0); + + /* + * |#########|$$$$$$$$$$|########|###| + * `tail `head `wakeup + * `limit + * [ count ] + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = handle.head + SZ_1M / 8; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 8); + + /* + * |####|####|$$$$$$$$$$|############| + * `tail `head + * `wakeup + * `limit + * [ count >>> + * >>> ] + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M + SZ_1M / 8; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 2); + + /* + * |#######|########|$$$$$$$$$$$$$$$$| + * `head `wakeup `>tail + * `limit + * [ count ] + */ + handle.head = SZ_1M; + handle.wakeup = SZ_1M + SZ_1M / 8; + handle.size = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 8); + + /* + * |#######|$$$$$$$$$$$$$$$$$|#######| + * `tail `head + * `wakeup + * `limit + * [ count > + * >>> ] + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M + SZ_1M / 4; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 2); + + /* + * |#######|$$$$$$$$|$$$$$$$$|#######| + * `tail `wakeup `head + * `limit + * [ count > + * >>> ] + */ + handle.head = SZ_1M * 3 / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M + SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 2); + + /* + * |$$$$$$$|########|########|$$$$$$$| + * `head `wakeup `tail + * `limit + * [ count ] + */ + handle.head = SZ_1M / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M / 2; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4); + KUNIT_ASSERT_EQ(test, count, SZ_1M / 4); + + /* + * |$$$$$$$|#################|$$$$$$$| + * `head `tail + * `wakeup + * `limit + */ + handle.head = SZ_1M / 4; + handle.size = SZ_1M / 2; + handle.wakeup = SZ_1M * 3 / 4; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4); + KUNIT_ASSERT_EQ(test, count, 0); + + /* + * |$$$$$$$|#################|$$$$$$$| + * `wakeup `head `tail + * `limit + */ + handle.head = SZ_1M / 4; + handle.size = SZ_1M / 2; + handle.wakeup = 0; + + limit = __trbe_normal_offset(&handle); + buf.trbe_limit = limit; + count = __trbe_normal_trigger_count(&handle); + + KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4); + KUNIT_ASSERT_EQ(test, count, 0); +} + +static struct kunit_case coresight_trbe_testcases[] = { + KUNIT_CASE(test_compute_offset), + KUNIT_CASE(test_compute_offset_and_counter), + {} +}; + +static struct kunit_suite coresight_trbe_test_suite = { + .name = "coresight_trbe_test_suite", + .test_cases = coresight_trbe_testcases, +}; + +kunit_test_suites(&coresight_trbe_test_suite); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Leo Yan leo.yan@arm.com"); +MODULE_DESCRIPTION("Arm CoreSight TRBE KUnit tests"); diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index ee9993d518d2a41f0d709b7d0690b2dfe0bef2d9..25d42683ab74b55efa2e19a2d77ab8ae2d68d228 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -54,92 +54,12 @@ enum trbe_fault_action { TRBE_FAULT_ACT_FATAL, };
-struct trbe_buf { - /* - * Even though trbe_base represents vmap() - * mapped allocated buffer's start address, - * it's being as unsigned long for various - * arithmetic and comparision operations & - * also to be consistent with trbe_write & - * trbe_limit sibling pointers. - */ - unsigned long trbe_base; - /* The base programmed into the TRBE */ - unsigned long trbe_hw_base; - unsigned long trbe_limit; - unsigned long trbe_write; - unsigned long trbe_count; - int nr_pages; - void **pages; - bool snapshot; - struct trbe_cpudata *cpudata; -}; - -/* - * TRBE erratum list - * - * The errata are defined in arm64 generic cpu_errata framework. - * Since the errata work arounds could be applied individually - * to the affected CPUs inside the TRBE driver, we need to know if - * a given CPU is affected by the erratum. Unlike the other erratum - * work arounds, TRBE driver needs to check multiple times during - * a trace session. Thus we need a quicker access to per-CPU - * errata and not issue costly this_cpu_has_cap() everytime. - * We keep a set of the affected errata in trbe_cpudata, per TRBE. - * - * We rely on the corresponding cpucaps to be defined for a given - * TRBE erratum. We map the given cpucap into a TRBE internal number - * to make the tracking of the errata lean. - * - * This helps in : - * - Not duplicating the detection logic - * - Streamlined detection of erratum across the system - */ -#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0 -#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE 1 -#define TRBE_NEEDS_DRAIN_AFTER_DISABLE 2 -#define TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE 3 -#define TRBE_IS_BROKEN 4 - -static int trbe_errata_cpucaps[] = { - [TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE, - [TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE, - [TRBE_NEEDS_DRAIN_AFTER_DISABLE] = ARM64_WORKAROUND_2064142, - [TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE] = ARM64_WORKAROUND_2038923, - [TRBE_IS_BROKEN] = ARM64_WORKAROUND_1902691, - -1, /* Sentinel, must be the last entry */ -}; - -/* The total number of listed errata in trbe_errata_cpucaps */ -#define TRBE_ERRATA_MAX (ARRAY_SIZE(trbe_errata_cpucaps) - 1) - /* * Safe limit for the number of bytes that may be overwritten * when ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE is triggered. */ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
-/* - * struct trbe_cpudata: TRBE instance specific data - * @trbe_flag - TRBE dirty/access flag support - * @trbe_hw_align - Actual TRBE alignment required for TRBPTR_EL1. - * @trbe_align - Software alignment used for the TRBPTR_EL1. - * @cpu - CPU this TRBE belongs to. - * @mode - Mode of current operation. (perf/disabled) - * @drvdata - TRBE specific drvdata - * @errata - Bit map for the errata on this TRBE. - */ -struct trbe_cpudata { - bool trbe_flag; - u64 trbe_hw_align; - u64 trbe_align; - int cpu; - enum cs_mode mode; - struct trbe_buf *buf; - struct trbe_drvdata *drvdata; - DECLARE_BITMAP(errata, TRBE_ERRATA_MAX); -}; - struct trbe_drvdata { struct trbe_cpudata __percpu *cpudata; struct perf_output_handle * __percpu *handle; @@ -150,7 +70,8 @@ struct trbe_drvdata { struct platform_device *pdev; };
-DEFINE_STATIC_KEY_FALSE(trbe_trigger_mode_bypass); +VISIBLE_IF_KUNIT DEFINE_STATIC_KEY_FALSE(trbe_trigger_mode_bypass); +EXPORT_SYMBOL_IF_KUNIT(trbe_trigger_mode_bypass);
#define trbe_trigger_mode_need_bypass(cpudata) \ (trbe_may_overwrite_in_fill_mode((cpudata)) || \ @@ -333,8 +254,17 @@ static void __trbe_pad_buf(struct trbe_buf *buf, u64 offset, int len)
static void trbe_pad_buf(struct perf_output_handle *handle, int len) { - struct trbe_buf *buf = etm_perf_sink_config(handle); - u64 head = PERF_IDX2OFF(handle->head, buf); + struct trbe_buf *buf; + u64 head; + + if (kunit_get_current_test()) { + handle->head += len; + handle->size -= len; + return; + } + + buf = etm_perf_sink_config(handle); + head = PERF_IDX2OFF(handle->head, buf);
__trbe_pad_buf(buf, head, len); if (!buf->snapshot) @@ -383,9 +313,11 @@ static u64 trbe_min_trace_buf_size(struct perf_output_handle *handle) * %%%% - Free area, disabled, trace will not be written * ==== - Free area, padded with ETE_IGNORE_PACKET, trace will be skipped */ -static unsigned long __trbe_normal_offset(struct perf_output_handle *handle) +VISIBLE_IF_KUNIT +unsigned long __trbe_normal_offset(struct perf_output_handle *handle) { - struct trbe_buf *buf = etm_perf_sink_config(handle); + struct trbe_buf *buf = + kunit_get_current_test() ? handle->rb : etm_perf_sink_config(handle); struct trbe_cpudata *cpudata = buf->cpudata; const u64 bufsize = buf->nr_pages * PAGE_SIZE; u64 limit = bufsize; @@ -525,9 +457,13 @@ static unsigned long __trbe_normal_offset(struct perf_output_handle *handle) return 0; }
-static u64 __trbe_normal_trigger_count(struct perf_output_handle *handle) +EXPORT_SYMBOL_IF_KUNIT(__trbe_normal_offset); + +VISIBLE_IF_KUNIT +u64 __trbe_normal_trigger_count(struct perf_output_handle *handle) { - struct trbe_buf *buf = etm_perf_sink_config(handle); + struct trbe_buf *buf = + kunit_get_current_test() ? handle->rb : etm_perf_sink_config(handle); struct trbe_cpudata *cpudata = buf->cpudata; u64 limit, head, wakeup; u64 count = 0; @@ -558,6 +494,8 @@ static u64 __trbe_normal_trigger_count(struct perf_output_handle *handle) return count; }
+EXPORT_SYMBOL_IF_KUNIT(__trbe_normal_trigger_count); + static int trbe_normal_offset(struct perf_output_handle *handle) { struct trbe_buf *buf = etm_perf_sink_config(handle); diff --git a/drivers/hwtracing/coresight/coresight-trbe.h b/drivers/hwtracing/coresight/coresight-trbe.h index 4c65d164a946ec9860825e7564196745b60d730b..8f90836b5f71d44213699ec1915d59864863a4db 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.h +++ b/drivers/hwtracing/coresight/coresight-trbe.h @@ -17,8 +17,91 @@ #include <linux/platform_device.h> #include <linux/smp.h>
+#include <kunit/test-bug.h> +#include <kunit/visibility.h> + #include "coresight-etm-perf.h"
+struct trbe_buf { + /* + * Even though trbe_base represents vmap() + * mapped allocated buffer's start address, + * it's being as unsigned long for various + * arithmetic and comparision operations & + * also to be consistent with trbe_write & + * trbe_limit sibling pointers. + */ + unsigned long trbe_base; + /* The base programmed into the TRBE */ + unsigned long trbe_hw_base; + unsigned long trbe_limit; + unsigned long trbe_write; + unsigned long trbe_count; + int nr_pages; + void **pages; + bool snapshot; + struct trbe_cpudata *cpudata; +}; + +/* + * TRBE erratum list + * + * The errata are defined in arm64 generic cpu_errata framework. + * Since the errata work arounds could be applied individually + * to the affected CPUs inside the TRBE driver, we need to know if + * a given CPU is affected by the erratum. Unlike the other erratum + * work arounds, TRBE driver needs to check multiple times during + * a trace session. Thus we need a quicker access to per-CPU + * errata and not issue costly this_cpu_has_cap() everytime. + * We keep a set of the affected errata in trbe_cpudata, per TRBE. + * + * We rely on the corresponding cpucaps to be defined for a given + * TRBE erratum. We map the given cpucap into a TRBE internal number + * to make the tracking of the errata lean. + * + * This helps in : + * - Not duplicating the detection logic + * - Streamlined detection of erratum across the system + */ +#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0 +#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE 1 +#define TRBE_NEEDS_DRAIN_AFTER_DISABLE 2 +#define TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE 3 +#define TRBE_IS_BROKEN 4 + +static int trbe_errata_cpucaps[] = { + [TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE, + [TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE, + [TRBE_NEEDS_DRAIN_AFTER_DISABLE] = ARM64_WORKAROUND_2064142, + [TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE] = ARM64_WORKAROUND_2038923, + [TRBE_IS_BROKEN] = ARM64_WORKAROUND_1902691, + -1, /* Sentinel, must be the last entry */ +}; + +/* The total number of listed errata in trbe_errata_cpucaps */ +#define TRBE_ERRATA_MAX (ARRAY_SIZE(trbe_errata_cpucaps) - 1) + +/* + * struct trbe_cpudata: TRBE instance specific data + * @trbe_flag - TRBE dirty/access flag support + * @trbe_hw_align - Actual TRBE alignment required for TRBPTR_EL1. + * @trbe_align - Software alignment used for the TRBPTR_EL1. + * @cpu - CPU this TRBE belongs to. + * @mode - Mode of current operation. (perf/disabled) + * @drvdata - TRBE specific drvdata + * @errata - Bit map for the errata on this TRBE. + */ +struct trbe_cpudata { + bool trbe_flag; + u64 trbe_hw_align; + u64 trbe_align; + int cpu; + enum cs_mode mode; + struct trbe_buf *buf; + struct trbe_drvdata *drvdata; + DECLARE_BITMAP(errata, TRBE_ERRATA_MAX); +}; + static inline bool is_trbe_available(void) { u64 aa64dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1); @@ -153,3 +236,9 @@ static inline void set_trbe_base_pointer(unsigned long addr) WARN_ON(!IS_ALIGNED(addr, PAGE_SIZE)); write_sysreg_s(addr, SYS_TRBBASER_EL1); } + +#if IS_ENABLED(CONFIG_KUNIT) +DECLARE_STATIC_KEY_FALSE(trbe_trigger_mode_bypass); +unsigned long __trbe_normal_offset(struct perf_output_handle *handle); +u64 __trbe_normal_trigger_count(struct perf_output_handle *handle); +#endif