summaryrefslogtreecommitdiff
path: root/lib/tests/test_ratelimit.c
blob: bfaeca49304a556701f6193f193816deb24b6e27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// SPDX-License-Identifier: GPL-2.0-only

#include <kunit/test.h>

#include <linux/ratelimit.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/cpumask.h>

/* a simple boot-time regression test */

#define TESTRL_INTERVAL (5 * HZ)
static DEFINE_RATELIMIT_STATE(testrl, TESTRL_INTERVAL, 3);

#define test_ratelimited(test, expected) \
	KUNIT_ASSERT_EQ(test, ___ratelimit(&testrl, "test_ratelimit_smoke"), (expected))

static void test_ratelimit_smoke(struct kunit *test)
{
	// Check settings.
	KUNIT_ASSERT_GE(test, TESTRL_INTERVAL, 100);

	// Test normal operation.
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, false);

	schedule_timeout_idle(TESTRL_INTERVAL / 2);
	test_ratelimited(test, false);

	schedule_timeout_idle(TESTRL_INTERVAL * 3 / 4);
	test_ratelimited(test, true);

	schedule_timeout_idle(2 * TESTRL_INTERVAL);
	test_ratelimited(test, true);
	test_ratelimited(test, true);

	schedule_timeout_idle(TESTRL_INTERVAL / 2 );
	test_ratelimited(test, true);
	schedule_timeout_idle(TESTRL_INTERVAL * 3 / 4);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, false);

	// Test disabling.
	testrl.burst = 0;
	test_ratelimited(test, false);
	testrl.burst = 2;
	testrl.interval = 0;
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, true);

	// Testing re-enabling.
	testrl.interval = TESTRL_INTERVAL;
	test_ratelimited(test, true);
	test_ratelimited(test, true);
	test_ratelimited(test, false);
	test_ratelimited(test, false);
}

static struct ratelimit_state stressrl = RATELIMIT_STATE_INIT_FLAGS("stressrl", HZ / 10, 3,
								    RATELIMIT_MSG_ON_RELEASE);

static int doneflag;
static const int stress_duration = 2 * HZ;

struct stress_kthread {
	unsigned long nattempts;
	unsigned long nunlimited;
	unsigned long nlimited;
	unsigned long nmissed;
	struct task_struct *tp;
};

static int test_ratelimit_stress_child(void *arg)
{
	struct stress_kthread *sktp = arg;

	set_user_nice(current, MAX_NICE);
	WARN_ON_ONCE(!sktp->tp);

	while (!READ_ONCE(doneflag)) {
		sktp->nattempts++;
		if (___ratelimit(&stressrl, __func__))
			sktp->nunlimited++;
		else
			sktp->nlimited++;
		cond_resched();
	}

	sktp->nmissed = ratelimit_state_reset_miss(&stressrl);
	return 0;
}

static void test_ratelimit_stress(struct kunit *test)
{
	int i;
	const int n_stress_kthread = cpumask_weight(cpu_online_mask);
	struct stress_kthread skt = { 0 };
	struct stress_kthread *sktp = kcalloc(n_stress_kthread, sizeof(*sktp), GFP_KERNEL);

	KUNIT_EXPECT_NOT_NULL_MSG(test, sktp, "Memory allocation failure");
	for (i = 0; i < n_stress_kthread; i++) {
		sktp[i].tp = kthread_run(test_ratelimit_stress_child, &sktp[i], "%s/%i",
					 "test_ratelimit_stress_child", i);
		KUNIT_EXPECT_NOT_NULL_MSG(test, sktp, "kthread creation failure");
		pr_alert("Spawned test_ratelimit_stress_child %d\n", i);
	}
	schedule_timeout_idle(stress_duration);
	WRITE_ONCE(doneflag, 1);
	for (i = 0; i < n_stress_kthread; i++) {
		kthread_stop(sktp[i].tp);
		skt.nattempts += sktp[i].nattempts;
		skt.nunlimited += sktp[i].nunlimited;
		skt.nlimited += sktp[i].nlimited;
		skt.nmissed += sktp[i].nmissed;
	}
	KUNIT_ASSERT_EQ_MSG(test, skt.nunlimited + skt.nlimited, skt.nattempts,
			    "Outcomes not equal to attempts");
	KUNIT_ASSERT_EQ_MSG(test, skt.nlimited, skt.nmissed, "Misses not equal to limits");
}

static struct kunit_case ratelimit_test_cases[] = {
	KUNIT_CASE_SLOW(test_ratelimit_smoke),
	KUNIT_CASE_SLOW(test_ratelimit_stress),
	{}
};

static struct kunit_suite ratelimit_test_suite = {
	.name = "lib_ratelimit",
	.test_cases = ratelimit_test_cases,
};

kunit_test_suites(&ratelimit_test_suite);

MODULE_DESCRIPTION("___ratelimit() KUnit test suite");
MODULE_LICENSE("GPL");