slub_kunit uses hardware perf events to invoke _nolock() APIs from NMI
context. However, creating a HW perf event fails when there is no
hardware PMU, or when running in a virtualized environment without a
virtual PMU configured.

Since tests are often performed in virtualized environments, fall back
to a SW perf event so that the test can still run, even if it cannot
cover IRQ-disabled sections.

For the sheaves path, IRQs are not disabled until the barn is accessed.
So some coverage is still expected.

Signed-off-by: Harry Yoo (Oracle) <[email protected]>
---
 lib/tests/slub_kunit.c | 70 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 55 insertions(+), 15 deletions(-)

diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index fa6d31dbca16..11255fc8eb78 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -303,6 +303,7 @@ struct test_nolock_context {
        int alloc_ok;
        int alloc_fail;
        struct perf_event *event;
+       bool is_perf_type_hw;
 };
 
 static struct perf_event_attr hw_attr = {
@@ -315,9 +316,19 @@ static struct perf_event_attr hw_attr = {
        .sample_freq = 100000,
 };
 
-static void overflow_handler_test_kmalloc_kfree_nolock(struct perf_event 
*event,
-                                                      struct perf_sample_data 
*data,
-                                                      struct pt_regs *regs)
+/* Fallback when hardware perf event is not available */
+static struct perf_event_attr sw_attr = {
+       .type = PERF_TYPE_SOFTWARE,
+       .config = PERF_COUNT_SW_CPU_CLOCK,
+       .size = sizeof(struct perf_event_attr),
+       .disabled = 1,
+       .freq = 1,
+       .sample_freq = 100000,
+};
+
+static void overflow_handler_test_nolock(struct perf_event *event,
+                                        struct perf_sample_data *data,
+                                        struct pt_regs *regs)
 {
        void *objp;
        gfp_t gfp;
@@ -336,20 +347,53 @@ static void 
overflow_handler_test_kmalloc_kfree_nolock(struct perf_event *event,
        ctx->callback_count++;
 }
 
+static bool enable_perf_events(struct test_nolock_context *ctx)
+{
+       struct perf_event *event;
+
+       ctx->is_perf_type_hw = true;
+       event = perf_event_create_kernel_counter(&hw_attr, -1, current,
+                                                overflow_handler_test_nolock,
+                                                ctx);
+
+       if (!IS_ERR(event))
+               goto out;
+
+       ctx->is_perf_type_hw = false;
+       event = perf_event_create_kernel_counter(&sw_attr, -1, current,
+                                                overflow_handler_test_nolock,
+                                                ctx);
+       if (!IS_ERR(event))
+               goto out;
+
+       return false;
+out:
+       ctx->event = event;
+       perf_event_enable(ctx->event);
+       return true;
+}
+
+static void disable_perf_events(struct test_nolock_context *ctx)
+{
+       kunit_info(ctx->test, "%s perf events: callback_count: %d, alloc_ok: 
%d, alloc_fail: %d\n",
+                  ctx->is_perf_type_hw ? "HW" : "SW",
+                  ctx->callback_count, ctx->alloc_ok, ctx->alloc_fail);
+
+       perf_event_disable(ctx->event);
+       perf_event_release_kernel(ctx->event);
+}
+
 static void test_kmalloc_kfree_nolock(struct kunit *test)
 {
        int i, j;
        struct test_nolock_context ctx = { .test = test };
-       struct perf_event *event;
        bool alloc_fail = false;
+       bool perf_events_enabled;
 
-       event = perf_event_create_kernel_counter(&hw_attr, -1, current,
-                                                
overflow_handler_test_kmalloc_kfree_nolock,
-                                                &ctx);
-       if (IS_ERR(event))
+       perf_events_enabled = enable_perf_events(&ctx);
+       if (!perf_events_enabled)
                kunit_skip(test, "Failed to create perf event");
-       ctx.event = event;
-       perf_event_enable(ctx.event);
+
        for (i = 0; i < NR_ITERATIONS; i++) {
                for (j = 0; j < NR_OBJECTS; j++) {
                        gfp_t gfp = (i % 2) ? GFP_KERNEL : GFP_KERNEL_ACCOUNT;
@@ -368,11 +412,7 @@ static void test_kmalloc_kfree_nolock(struct kunit *test)
        }
 
 cleanup:
-       perf_event_disable(ctx.event);
-       perf_event_release_kernel(ctx.event);
-
-       kunit_info(test, "callback_count: %d, alloc_ok: %d, alloc_fail: %d\n",
-                  ctx.callback_count, ctx.alloc_ok, ctx.alloc_fail);
+       disable_perf_events(&ctx);
 
        if (alloc_fail)
                kunit_skip(test, "Allocation failed");

-- 
2.53.0


Reply via email to