summaryrefslogtreecommitdiff
path: root/lib/tests/errseq_kunit.c
blob: 8f39ebc4a2488e564efe216883008b0d96a75fe6 (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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// SPDX-License-Identifier: GPL-2.0
/*
 * KUnit tests for the errseq_t error-tracking infrastructure.
 *
 * These exercise the documented single-threaded semantics of the errseq
 * API (see Documentation/core-api/errseq.rst and lib/errseq.c): error
 * recording and overwriting, the "seen" handoff between errseq_sample()
 * and errseq_check_and_advance(), and the re-reporting of an error that
 * is recorded again after it has been seen.
 *
 * The lockless properties of errseq_t under concurrent updates are
 * outside the scope of these deterministic tests, as is the WARN path
 * for invalid error values.
 */
#include <kunit/test.h>

#include <linux/err.h>
#include <linux/errno.h>
#include <linux/errseq.h>

/*
 * A zeroed errseq_t is the "no error has ever occurred" epoch: it
 * samples as zero and no check against it reports anything.
 */
static void errseq_test_zero_epoch_reports_no_error(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since = 0;

	KUNIT_EXPECT_EQ(test, errseq_sample(&eseq), 0);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), 0);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0);
	KUNIT_EXPECT_EQ(test, since, 0);
}

static void errseq_test_set_records_error(struct kunit *test)
{
	errseq_t eseq = 0;

	/* errseq_set() returns the previous value; the epoch is zero. */
	KUNIT_EXPECT_EQ(test, errseq_set(&eseq, -EIO), 0);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), -EIO);
}

/* Any error set always overwrites an existing error. */
static void errseq_test_set_overwrites_error(struct kunit *test)
{
	errseq_t eseq = 0;

	errseq_set(&eseq, -EIO);
	errseq_set(&eseq, -ENOSPC);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), -ENOSPC);
}

/* Both ends of the valid error range are recorded exactly. */
static void errseq_test_errno_range_extremes(struct kunit *test)
{
	errseq_t lo = 0;
	errseq_t hi = 0;

	errseq_set(&lo, -1);
	KUNIT_EXPECT_EQ(test, errseq_check(&lo, 0), -1);

	errseq_set(&hi, -MAX_ERRNO);
	KUNIT_EXPECT_EQ(test, errseq_check(&hi, 0), -MAX_ERRNO);
}

/*
 * An error nobody has seen yet samples as zero, so that a check against
 * the sample still reports it (see commit b4678df184b3 ("errseq: Always
 * report a writeback error once")).
 */
static void errseq_test_sample_of_unseen_error_is_zero(struct kunit *test)
{
	errseq_t eseq = 0;

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_sample(&eseq), 0);
}

static void errseq_test_new_sampler_sees_unseen_error(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since;

	errseq_set(&eseq, -EIO);
	since = errseq_sample(&eseq);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, since), -EIO);
}

/* A given error is reported exactly once per advancing cursor. */
static void errseq_test_check_and_advance_reports_once(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since = errseq_sample(&eseq);

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0);
}

/*
 * Once an error has been seen, a fresh sample is non-zero and checking
 * against it reports nothing: handled errors do not reach new samplers.
 */
static void errseq_test_sample_after_seen_is_current(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since = 0;
	errseq_t sample;

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO);

	sample = errseq_sample(&eseq);
	KUNIT_EXPECT_NE(test, sample, 0);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, sample), 0);
}

static void errseq_test_new_error_after_advance(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since = 0;

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO);

	errseq_set(&eseq, -ENOSPC);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -ENOSPC);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0);
}

/*
 * Recording the same error again after it has been seen must bump the
 * sequence, so cursors that consumed the first occurrence see the
 * second one too.
 */
static void errseq_test_same_error_reported_again_after_seen(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since = 0;
	errseq_t seen_cursor;

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO);

	seen_cursor = since;
	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, since), -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO);
	/* The repeat must advance the sequence, not just re-toggle "seen". */
	KUNIT_EXPECT_NE(test, since, seen_cursor);
}

/*
 * A cursor that consumed an error must still observe a repeat of that
 * error even when another cursor has already marked the repeat seen:
 * recording over a seen value must advance the sequence.
 */
static void errseq_test_repeat_error_visible_to_all_cursors(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t cursor_a = 0;
	errseq_t cursor_b = 0;

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO);

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), -EIO);

	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, cursor_a), -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), 0);
}

/* An advance with no new error reports nothing and leaves the cursor put. */
static void errseq_test_advance_stable_when_unchanged(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t since = 0;
	errseq_t cursor;

	errseq_set(&eseq, -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO);

	cursor = since;
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0);
	KUNIT_EXPECT_EQ(test, since, cursor);
}

/*
 * Cursors are independent: one subscriber consuming an error does not
 * consume it for another, and each subscriber sees each error once.
 */
static void errseq_test_two_subscribers_independent(struct kunit *test)
{
	errseq_t eseq = 0;
	errseq_t cursor_a = errseq_sample(&eseq);
	errseq_t cursor_b = errseq_sample(&eseq);

	errseq_set(&eseq, -EIO);

	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check(&eseq, cursor_b), -EIO);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), -EIO);

	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), 0);
	KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), 0);
}

static struct kunit_case errseq_test_cases[] = {
	KUNIT_CASE(errseq_test_zero_epoch_reports_no_error),
	KUNIT_CASE(errseq_test_set_records_error),
	KUNIT_CASE(errseq_test_set_overwrites_error),
	KUNIT_CASE(errseq_test_errno_range_extremes),
	KUNIT_CASE(errseq_test_sample_of_unseen_error_is_zero),
	KUNIT_CASE(errseq_test_new_sampler_sees_unseen_error),
	KUNIT_CASE(errseq_test_check_and_advance_reports_once),
	KUNIT_CASE(errseq_test_sample_after_seen_is_current),
	KUNIT_CASE(errseq_test_new_error_after_advance),
	KUNIT_CASE(errseq_test_same_error_reported_again_after_seen),
	KUNIT_CASE(errseq_test_repeat_error_visible_to_all_cursors),
	KUNIT_CASE(errseq_test_advance_stable_when_unchanged),
	KUNIT_CASE(errseq_test_two_subscribers_independent),
	{}
};

static struct kunit_suite errseq_test_suite = {
	.name = "errseq",
	.test_cases = errseq_test_cases,
};

kunit_test_suite(errseq_test_suite);

MODULE_DESCRIPTION("KUnit tests for the errseq infrastructure");
MODULE_LICENSE("GPL");