Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.14.15.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * test_fprobe.c - simple sanity test for fprobe
  4 */
  5
  6#include <linux/kernel.h>
  7#include <linux/fprobe.h>
  8#include <linux/random.h>
  9#include <kunit/test.h>
 10
 11#define div_factor 3
 12
 13static struct kunit *current_test;
 14
 15static u32 rand1, entry_val, exit_val;
 16
 17/* Use indirect calls to avoid inlining the target functions */
 18static u32 (*target)(u32 value);
 19static u32 (*target2)(u32 value);
 20static unsigned long target_ip;
 21static unsigned long target2_ip;
 22
 23static noinline u32 fprobe_selftest_target(u32 value)
 24{
 25	return (value / div_factor);
 26}
 27
 28static noinline u32 fprobe_selftest_target2(u32 value)
 29{
 30	return (value / div_factor) + 1;
 31}
 32
 33static notrace void fp_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
 34{
 35	KUNIT_EXPECT_FALSE(current_test, preemptible());
 36	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
 37	if (ip != target_ip)
 38		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
 39	entry_val = (rand1 / div_factor);
 40}
 41
 42static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
 43{
 44	unsigned long ret = regs_return_value(regs);
 45
 46	KUNIT_EXPECT_FALSE(current_test, preemptible());
 47	if (ip != target_ip) {
 48		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
 49		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1);
 50	} else
 51		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor));
 52	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));
 53	exit_val = entry_val + div_factor;
 54}
 55
 56/* Test entry only (no rethook) */
 57static void test_fprobe_entry(struct kunit *test)
 58{
 59	struct fprobe fp_entry = {
 60		.entry_handler = fp_entry_handler,
 61	};
 62
 63	current_test = test;
 64
 65	/* Before register, unregister should be failed. */
 66	KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry));
 67	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL));
 68
 69	entry_val = 0;
 70	exit_val = 0;
 71	target(rand1);
 72	KUNIT_EXPECT_NE(test, 0, entry_val);
 73	KUNIT_EXPECT_EQ(test, 0, exit_val);
 74
 75	entry_val = 0;
 76	exit_val = 0;
 77	target2(rand1);
 78	KUNIT_EXPECT_NE(test, 0, entry_val);
 79	KUNIT_EXPECT_EQ(test, 0, exit_val);
 80
 81	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry));
 82}
 83
 84static void test_fprobe(struct kunit *test)
 85{
 86	struct fprobe fp = {
 87		.entry_handler = fp_entry_handler,
 88		.exit_handler = fp_exit_handler,
 89	};
 90
 91	current_test = test;
 92	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL));
 93
 94	entry_val = 0;
 95	exit_val = 0;
 96	target(rand1);
 97	KUNIT_EXPECT_NE(test, 0, entry_val);
 98	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
 99
100	entry_val = 0;
101	exit_val = 0;
102	target2(rand1);
103	KUNIT_EXPECT_NE(test, 0, entry_val);
104	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
105
106	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
107}
108
109static void test_fprobe_syms(struct kunit *test)
110{
111	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"};
112	struct fprobe fp = {
113		.entry_handler = fp_entry_handler,
114		.exit_handler = fp_exit_handler,
115	};
116
117	current_test = test;
118	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
119
120	entry_val = 0;
121	exit_val = 0;
122	target(rand1);
123	KUNIT_EXPECT_NE(test, 0, entry_val);
124	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
125
126	entry_val = 0;
127	exit_val = 0;
128	target2(rand1);
129	KUNIT_EXPECT_NE(test, 0, entry_val);
130	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
131
132	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
133}
134
135static unsigned long get_ftrace_location(void *func)
136{
137	unsigned long size, addr = (unsigned long)func;
138
139	if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
140		return 0;
141
142	return ftrace_location_range(addr, addr + size - 1);
143}
144
145static int fprobe_test_init(struct kunit *test)
146{
147	rand1 = get_random_u32_above(div_factor);
148	target = fprobe_selftest_target;
149	target2 = fprobe_selftest_target2;
150	target_ip = get_ftrace_location(target);
151	target2_ip = get_ftrace_location(target2);
152
153	return 0;
154}
155
156static struct kunit_case fprobe_testcases[] = {
157	KUNIT_CASE(test_fprobe_entry),
158	KUNIT_CASE(test_fprobe),
159	KUNIT_CASE(test_fprobe_syms),
160	{}
161};
162
163static struct kunit_suite fprobe_test_suite = {
164	.name = "fprobe_test",
165	.init = fprobe_test_init,
166	.test_cases = fprobe_testcases,
167};
168
169kunit_test_suites(&fprobe_test_suite);
170
171MODULE_LICENSE("GPL");