s390/test_unwind: convert to KUnit

Modified stack unwinder self tests to use kunit framework. The
functionality stayed the same but the output format is now in tap13
format.

Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
Mete Durlu 2021-10-01 15:52:00 +02:00 committed by Vasily Gorbik
parent 4a667ba873
commit 25d36a85c6
2 changed files with 98 additions and 73 deletions

View File

@ -938,6 +938,8 @@ menu "Selftests"
config S390_UNWIND_SELFTEST config S390_UNWIND_SELFTEST
def_tristate n def_tristate n
depends on KUNIT
default KUNIT_ALL_TESTS
prompt "Test unwind functions" prompt "Test unwind functions"
help help
This option enables s390 specific stack unwinder testing kernel This option enables s390 specific stack unwinder testing kernel

View File

@ -3,7 +3,7 @@
* Test module for unwind_for_each_frame * Test module for unwind_for_each_frame
*/ */
#define pr_fmt(fmt) "test_unwind: " fmt #include <kunit/test.h>
#include <asm/unwind.h> #include <asm/unwind.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
@ -16,6 +16,8 @@
#include <linux/wait.h> #include <linux/wait.h>
#include <asm/irq.h> #include <asm/irq.h>
struct kunit *current_test;
#define BT_BUF_SIZE (PAGE_SIZE * 4) #define BT_BUF_SIZE (PAGE_SIZE * 4)
/* /*
@ -29,7 +31,7 @@ static void print_backtrace(char *bt)
p = strsep(&bt, "\n"); p = strsep(&bt, "\n");
if (!p) if (!p)
break; break;
pr_err("%s\n", p); kunit_err(current_test, "%s\n", p);
} }
} }
@ -49,7 +51,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
bt = kmalloc(BT_BUF_SIZE, GFP_ATOMIC); bt = kmalloc(BT_BUF_SIZE, GFP_ATOMIC);
if (!bt) { if (!bt) {
pr_err("failed to allocate backtrace buffer\n"); kunit_err(current_test, "failed to allocate backtrace buffer\n");
return -ENOMEM; return -ENOMEM;
} }
/* Unwind. */ /* Unwind. */
@ -63,7 +65,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
if (frame_count++ == max_frames) if (frame_count++ == max_frames)
break; break;
if (state.reliable && !addr) { if (state.reliable && !addr) {
pr_err("unwind state reliable but addr is 0\n"); kunit_err(current_test, "unwind state reliable but addr is 0\n");
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
@ -75,7 +77,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
stack_type_name(state.stack_info.type), stack_type_name(state.stack_info.type),
(void *)state.sp, (void *)state.ip); (void *)state.sp, (void *)state.ip);
if (bt_pos >= BT_BUF_SIZE) if (bt_pos >= BT_BUF_SIZE)
pr_err("backtrace buffer is too small\n"); kunit_err(current_test, "backtrace buffer is too small\n");
} }
frame_count += 1; frame_count += 1;
if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1")) if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
@ -85,15 +87,15 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
/* Check the results. */ /* Check the results. */
if (unwind_error(&state)) { if (unwind_error(&state)) {
pr_err("unwind error\n"); kunit_err(current_test, "unwind error\n");
ret = -EINVAL; ret = -EINVAL;
} }
if (!seen_func2_func1) { if (!seen_func2_func1) {
pr_err("unwindme_func2 and unwindme_func1 not found\n"); kunit_err(current_test, "unwindme_func2 and unwindme_func1 not found\n");
ret = -EINVAL; ret = -EINVAL;
} }
if (frame_count == max_frames) { if (frame_count == max_frames) {
pr_err("Maximum number of frames exceeded\n"); kunit_err(current_test, "Maximum number of frames exceeded\n");
ret = -EINVAL; ret = -EINVAL;
} }
if (ret) if (ret)
@ -166,7 +168,7 @@ static noinline int unwindme_func4(struct unwindme *u)
kp.pre_handler = pgm_pre_handler; kp.pre_handler = pgm_pre_handler;
ret = register_kprobe(&kp); ret = register_kprobe(&kp);
if (ret < 0) { if (ret < 0) {
pr_err("register_kprobe failed %d\n", ret); kunit_err(current_test, "register_kprobe failed %d\n", ret);
return -EINVAL; return -EINVAL;
} }
@ -252,7 +254,7 @@ static int test_unwind_irq(struct unwindme *u)
} }
/* Spawns a task and passes it to test_unwind(). */ /* Spawns a task and passes it to test_unwind(). */
static int test_unwind_task(struct unwindme *u) static int test_unwind_task(struct kunit *test, struct unwindme *u)
{ {
struct task_struct *task; struct task_struct *task;
int ret; int ret;
@ -267,7 +269,7 @@ static int test_unwind_task(struct unwindme *u)
*/ */
task = kthread_run(unwindme_func1, u, "%s", __func__); task = kthread_run(unwindme_func1, u, "%s", __func__);
if (IS_ERR(task)) { if (IS_ERR(task)) {
pr_err("kthread_run() failed\n"); kunit_err(test, "kthread_run() failed\n");
return PTR_ERR(task); return PTR_ERR(task);
} }
/* /*
@ -282,77 +284,98 @@ static int test_unwind_task(struct unwindme *u)
return ret; return ret;
} }
static int test_unwind_flags(int flags) struct test_params {
int flags;
char *name;
};
/*
* Create required parameter list for tests
*/
static const struct test_params param_list[] = {
{.flags = UWM_DEFAULT, .name = "UWM_DEFAULT"},
{.flags = UWM_SP, .name = "UWM_SP"},
{.flags = UWM_REGS, .name = "UWM_REGS"},
{.flags = UWM_SWITCH_STACK,
.name = "UWM_SWITCH_STACK"},
{.flags = UWM_SP | UWM_REGS,
.name = "UWM_SP | UWM_REGS"},
{.flags = UWM_CALLER | UWM_SP,
.name = "WM_CALLER | UWM_SP"},
{.flags = UWM_CALLER | UWM_SP | UWM_REGS,
.name = "UWM_CALLER | UWM_SP | UWM_REGS"},
{.flags = UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK,
.name = "UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK"},
{.flags = UWM_THREAD, .name = "UWM_THREAD"},
{.flags = UWM_THREAD | UWM_SP,
.name = "UWM_THREAD | UWM_SP"},
{.flags = UWM_THREAD | UWM_CALLER | UWM_SP,
.name = "UWM_THREAD | UWM_CALLER | UWM_SP"},
{.flags = UWM_IRQ, .name = "UWM_IRQ"},
{.flags = UWM_IRQ | UWM_SWITCH_STACK,
.name = "UWM_IRQ | UWM_SWITCH_STACK"},
{.flags = UWM_IRQ | UWM_SP,
.name = "UWM_IRQ | UWM_SP"},
{.flags = UWM_IRQ | UWM_REGS,
.name = "UWM_IRQ | UWM_REGS"},
{.flags = UWM_IRQ | UWM_SP | UWM_REGS,
.name = "UWM_IRQ | UWM_SP | UWM_REGS"},
{.flags = UWM_IRQ | UWM_CALLER | UWM_SP,
.name = "UWM_IRQ | UWM_CALLER | UWM_SP"},
{.flags = UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS,
.name = "UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS"},
{.flags = UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK,
.name = "UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK"},
#ifdef CONFIG_KPROBES
{.flags = UWM_PGM, .name = "UWM_PGM"},
{.flags = UWM_PGM | UWM_SP,
.name = "UWM_PGM | UWM_SP"},
{.flags = UWM_PGM | UWM_REGS,
.name = "UWM_PGM | UWM_REGS"},
{.flags = UWM_PGM | UWM_SP | UWM_REGS,
.name = "UWM_PGM | UWM_SP | UWM_REGS"},
#endif
};
/*
* Parameter description generator: required for KUNIT_ARRAY_PARAM()
*/
static void get_desc(const struct test_params *params, char *desc)
{
strscpy(desc, params->name, KUNIT_PARAM_DESC_SIZE);
}
/*
* Create test_unwind_gen_params
*/
KUNIT_ARRAY_PARAM(test_unwind, param_list, get_desc);
static void test_unwind_flags(struct kunit *test)
{ {
struct unwindme u; struct unwindme u;
const struct test_params *params;
u.flags = flags; current_test = test;
params = (const struct test_params *)test->param_value;
u.flags = params->flags;
if (u.flags & UWM_THREAD) if (u.flags & UWM_THREAD)
return test_unwind_task(&u); KUNIT_EXPECT_EQ(test, 0, test_unwind_task(test, &u));
else if (u.flags & UWM_IRQ) else if (u.flags & UWM_IRQ)
return test_unwind_irq(&u); KUNIT_EXPECT_EQ(test, 0, test_unwind_irq(&u));
else else
return unwindme_func1(&u); KUNIT_EXPECT_EQ(test, 0, unwindme_func1(&u));
} }
static int test_unwind_init(void) static struct kunit_case unwind_test_cases[] = {
{ KUNIT_CASE_PARAM(test_unwind_flags, test_unwind_gen_params),
int failed = 0; {}
int total = 0; };
#define TEST(flags) \ static struct kunit_suite test_unwind_suite = {
do { \ .name = "test_unwind",
pr_info("[ RUN ] " #flags "\n"); \ .test_cases = unwind_test_cases,
total++; \ };
if (!test_unwind_flags((flags))) { \
pr_info("[ OK ] " #flags "\n"); \
} else { \
pr_err("[ FAILED ] " #flags "\n"); \
failed++; \
} \
} while (0)
pr_info("running stack unwinder tests"); kunit_test_suites(&test_unwind_suite);
TEST(UWM_DEFAULT);
TEST(UWM_SP);
TEST(UWM_REGS);
TEST(UWM_SWITCH_STACK);
TEST(UWM_SP | UWM_REGS);
TEST(UWM_CALLER | UWM_SP);
TEST(UWM_CALLER | UWM_SP | UWM_REGS);
TEST(UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK);
TEST(UWM_THREAD);
TEST(UWM_THREAD | UWM_SP);
TEST(UWM_THREAD | UWM_CALLER | UWM_SP);
TEST(UWM_IRQ);
TEST(UWM_IRQ | UWM_SWITCH_STACK);
TEST(UWM_IRQ | UWM_SP);
TEST(UWM_IRQ | UWM_REGS);
TEST(UWM_IRQ | UWM_SP | UWM_REGS);
TEST(UWM_IRQ | UWM_CALLER | UWM_SP);
TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS);
TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK);
#ifdef CONFIG_KPROBES
TEST(UWM_PGM);
TEST(UWM_PGM | UWM_SP);
TEST(UWM_PGM | UWM_REGS);
TEST(UWM_PGM | UWM_SP | UWM_REGS);
#endif
#undef TEST
if (failed) {
pr_err("%d of %d stack unwinder tests failed", failed, total);
WARN(1, "%d of %d stack unwinder tests failed", failed, total);
} else {
pr_info("all %d stack unwinder tests passed", total);
}
return failed ? -EINVAL : 0;
}
static void test_unwind_exit(void)
{
}
module_init(test_unwind_init);
module_exit(test_unwind_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");