Skip to content

Commit 1cbeab1

Browse files
yzaikin-at-googleshuahkh
authored andcommitted
ext4: add kunit test for decoding extended timestamps
KUnit tests for decoding extended 64 bit timestamps that verify the seconds part of [a/c/m] timestamps in ext4 inode structs are decoded correctly. Test data is derived from the table in the Inode Timestamps section of Documentation/filesystems/ext4/inodes.rst. KUnit tests run during boot and output the results to the debug log in TAP format (http://testanything.org/). Only useful for kernel devs running KUnit test harness and are not for inclusion into a production build. Signed-off-by: Iurii Zaikin <yzaikin@google.com> Reviewed-by: Theodore Ts'o <tytso@mit.edu> Reviewed-by: Brendan Higgins <brendanhiggins@google.com> Tested-by: Brendan Higgins <brendanhiggins@google.com> Reviewed-by: Shuah Khan <skhan@linuxfoundation.org> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent d460623 commit 1cbeab1

File tree

3 files changed

+290
-0
lines changed

3 files changed

+290
-0
lines changed

fs/ext4/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,20 @@ config EXT4_DEBUG
106106
If you select Y here, then you will be able to turn on debugging
107107
with a command such as:
108108
echo 1 > /sys/module/ext4/parameters/mballoc_debug
109+
110+
config EXT4_KUNIT_TESTS
111+
bool "KUnit tests for ext4"
112+
select EXT4_FS
113+
depends on KUNIT
114+
help
115+
This builds the ext4 KUnit tests.
116+
117+
KUnit tests run during boot and output the results to the debug log
118+
in TAP format (http://testanything.org/). Only useful for kernel devs
119+
running KUnit test harness and are not for inclusion into a production
120+
build.
121+
122+
For more information on KUnit and unit tests in general please refer
123+
to the KUnit documentation in Documentation/dev-tools/kunit/.
124+
125+
If unsure, say N.

fs/ext4/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \
1313

1414
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
1515
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
16+
ext4-$(CONFIG_EXT4_KUNIT_TESTS) += inode-test.o
1617
ext4-$(CONFIG_FS_VERITY) += verity.o

fs/ext4/inode-test.c

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* KUnit test of ext4 inode that verify the seconds part of [a/c/m]
4+
* timestamps in ext4 inode structs are decoded correctly.
5+
*/
6+
7+
#include <kunit/test.h>
8+
#include <linux/kernel.h>
9+
#include <linux/time64.h>
10+
11+
#include "ext4.h"
12+
13+
/*
14+
* For constructing the nonnegative timestamp lower bound value.
15+
* binary: 00000000 00000000 00000000 00000000
16+
*/
17+
#define LOWER_MSB_0 0L
18+
/*
19+
* For constructing the nonnegative timestamp upper bound value.
20+
* binary: 01111111 11111111 11111111 11111111
21+
*
22+
*/
23+
#define UPPER_MSB_0 0x7fffffffL
24+
/*
25+
* For constructing the negative timestamp lower bound value.
26+
* binary: 10000000 00000000 00000000 00000000
27+
*/
28+
#define LOWER_MSB_1 (-0x80000000L)
29+
/*
30+
* For constructing the negative timestamp upper bound value.
31+
* binary: 11111111 11111111 11111111 11111111
32+
*/
33+
#define UPPER_MSB_1 (-1L)
34+
/*
35+
* Upper bound for nanoseconds value supported by the encoding.
36+
* binary: 00111111 11111111 11111111 11111111
37+
*/
38+
#define MAX_NANOSECONDS ((1L << 30) - 1)
39+
40+
#define CASE_NAME_FORMAT "%s: msb:%x lower_bound:%x extra_bits: %x"
41+
42+
#define LOWER_BOUND_NEG_NO_EXTRA_BITS_CASE\
43+
"1901-12-13 Lower bound of 32bit < 0 timestamp, no extra bits"
44+
#define UPPER_BOUND_NEG_NO_EXTRA_BITS_CASE\
45+
"1969-12-31 Upper bound of 32bit < 0 timestamp, no extra bits"
46+
#define LOWER_BOUND_NONNEG_NO_EXTRA_BITS_CASE\
47+
"1970-01-01 Lower bound of 32bit >=0 timestamp, no extra bits"
48+
#define UPPER_BOUND_NONNEG_NO_EXTRA_BITS_CASE\
49+
"2038-01-19 Upper bound of 32bit >=0 timestamp, no extra bits"
50+
#define LOWER_BOUND_NEG_LO_1_CASE\
51+
"2038-01-19 Lower bound of 32bit <0 timestamp, lo extra sec bit on"
52+
#define UPPER_BOUND_NEG_LO_1_CASE\
53+
"2106-02-07 Upper bound of 32bit <0 timestamp, lo extra sec bit on"
54+
#define LOWER_BOUND_NONNEG_LO_1_CASE\
55+
"2106-02-07 Lower bound of 32bit >=0 timestamp, lo extra sec bit on"
56+
#define UPPER_BOUND_NONNEG_LO_1_CASE\
57+
"2174-02-25 Upper bound of 32bit >=0 timestamp, lo extra sec bit on"
58+
#define LOWER_BOUND_NEG_HI_1_CASE\
59+
"2174-02-25 Lower bound of 32bit <0 timestamp, hi extra sec bit on"
60+
#define UPPER_BOUND_NEG_HI_1_CASE\
61+
"2242-03-16 Upper bound of 32bit <0 timestamp, hi extra sec bit on"
62+
#define LOWER_BOUND_NONNEG_HI_1_CASE\
63+
"2242-03-16 Lower bound of 32bit >=0 timestamp, hi extra sec bit on"
64+
#define UPPER_BOUND_NONNEG_HI_1_CASE\
65+
"2310-04-04 Upper bound of 32bit >=0 timestamp, hi extra sec bit on"
66+
#define UPPER_BOUND_NONNEG_HI_1_NS_1_CASE\
67+
"2310-04-04 Upper bound of 32bit>=0 timestamp, hi extra sec bit 1. 1 ns"
68+
#define LOWER_BOUND_NONNEG_HI_1_NS_MAX_CASE\
69+
"2378-04-22 Lower bound of 32bit>= timestamp. Extra sec bits 1. Max ns"
70+
#define LOWER_BOUND_NONNEG_EXTRA_BITS_1_CASE\
71+
"2378-04-22 Lower bound of 32bit >=0 timestamp. All extra sec bits on"
72+
#define UPPER_BOUND_NONNEG_EXTRA_BITS_1_CASE\
73+
"2446-05-10 Upper bound of 32bit >=0 timestamp. All extra sec bits on"
74+
75+
struct timestamp_expectation {
76+
const char *test_case_name;
77+
struct timespec64 expected;
78+
u32 extra_bits;
79+
bool msb_set;
80+
bool lower_bound;
81+
};
82+
83+
static time64_t get_32bit_time(const struct timestamp_expectation * const test)
84+
{
85+
if (test->msb_set) {
86+
if (test->lower_bound)
87+
return LOWER_MSB_1;
88+
89+
return UPPER_MSB_1;
90+
}
91+
92+
if (test->lower_bound)
93+
return LOWER_MSB_0;
94+
return UPPER_MSB_0;
95+
}
96+
97+
98+
/*
99+
* Test data is derived from the table in the Inode Timestamps section of
100+
* Documentation/filesystems/ext4/inodes.rst.
101+
*/
102+
static void inode_test_xtimestamp_decoding(struct kunit *test)
103+
{
104+
const struct timestamp_expectation test_data[] = {
105+
{
106+
.test_case_name = LOWER_BOUND_NEG_NO_EXTRA_BITS_CASE,
107+
.msb_set = true,
108+
.lower_bound = true,
109+
.extra_bits = 0,
110+
.expected = {.tv_sec = -0x80000000LL, .tv_nsec = 0L},
111+
},
112+
113+
{
114+
.test_case_name = UPPER_BOUND_NEG_NO_EXTRA_BITS_CASE,
115+
.msb_set = true,
116+
.lower_bound = false,
117+
.extra_bits = 0,
118+
.expected = {.tv_sec = -1LL, .tv_nsec = 0L},
119+
},
120+
121+
{
122+
.test_case_name = LOWER_BOUND_NONNEG_NO_EXTRA_BITS_CASE,
123+
.msb_set = false,
124+
.lower_bound = true,
125+
.extra_bits = 0,
126+
.expected = {0LL, 0L},
127+
},
128+
129+
{
130+
.test_case_name = UPPER_BOUND_NONNEG_NO_EXTRA_BITS_CASE,
131+
.msb_set = false,
132+
.lower_bound = false,
133+
.extra_bits = 0,
134+
.expected = {.tv_sec = 0x7fffffffLL, .tv_nsec = 0L},
135+
},
136+
137+
{
138+
.test_case_name = LOWER_BOUND_NEG_LO_1_CASE,
139+
.msb_set = true,
140+
.lower_bound = true,
141+
.extra_bits = 1,
142+
.expected = {.tv_sec = 0x80000000LL, .tv_nsec = 0L},
143+
},
144+
145+
{
146+
.test_case_name = UPPER_BOUND_NEG_LO_1_CASE,
147+
.msb_set = true,
148+
.lower_bound = false,
149+
.extra_bits = 1,
150+
.expected = {.tv_sec = 0xffffffffLL, .tv_nsec = 0L},
151+
},
152+
153+
{
154+
.test_case_name = LOWER_BOUND_NONNEG_LO_1_CASE,
155+
.msb_set = false,
156+
.lower_bound = true,
157+
.extra_bits = 1,
158+
.expected = {.tv_sec = 0x100000000LL, .tv_nsec = 0L},
159+
},
160+
161+
{
162+
.test_case_name = UPPER_BOUND_NONNEG_LO_1_CASE,
163+
.msb_set = false,
164+
.lower_bound = false,
165+
.extra_bits = 1,
166+
.expected = {.tv_sec = 0x17fffffffLL, .tv_nsec = 0L},
167+
},
168+
169+
{
170+
.test_case_name = LOWER_BOUND_NEG_HI_1_CASE,
171+
.msb_set = true,
172+
.lower_bound = true,
173+
.extra_bits = 2,
174+
.expected = {.tv_sec = 0x180000000LL, .tv_nsec = 0L},
175+
},
176+
177+
{
178+
.test_case_name = UPPER_BOUND_NEG_HI_1_CASE,
179+
.msb_set = true,
180+
.lower_bound = false,
181+
.extra_bits = 2,
182+
.expected = {.tv_sec = 0x1ffffffffLL, .tv_nsec = 0L},
183+
},
184+
185+
{
186+
.test_case_name = LOWER_BOUND_NONNEG_HI_1_CASE,
187+
.msb_set = false,
188+
.lower_bound = true,
189+
.extra_bits = 2,
190+
.expected = {.tv_sec = 0x200000000LL, .tv_nsec = 0L},
191+
},
192+
193+
{
194+
.test_case_name = UPPER_BOUND_NONNEG_HI_1_CASE,
195+
.msb_set = false,
196+
.lower_bound = false,
197+
.extra_bits = 2,
198+
.expected = {.tv_sec = 0x27fffffffLL, .tv_nsec = 0L},
199+
},
200+
201+
{
202+
.test_case_name = UPPER_BOUND_NONNEG_HI_1_NS_1_CASE,
203+
.msb_set = false,
204+
.lower_bound = false,
205+
.extra_bits = 6,
206+
.expected = {.tv_sec = 0x27fffffffLL, .tv_nsec = 1L},
207+
},
208+
209+
{
210+
.test_case_name = LOWER_BOUND_NONNEG_HI_1_NS_MAX_CASE,
211+
.msb_set = false,
212+
.lower_bound = true,
213+
.extra_bits = 0xFFFFFFFF,
214+
.expected = {.tv_sec = 0x300000000LL,
215+
.tv_nsec = MAX_NANOSECONDS},
216+
},
217+
218+
{
219+
.test_case_name = LOWER_BOUND_NONNEG_EXTRA_BITS_1_CASE,
220+
.msb_set = false,
221+
.lower_bound = true,
222+
.extra_bits = 3,
223+
.expected = {.tv_sec = 0x300000000LL, .tv_nsec = 0L},
224+
},
225+
226+
{
227+
.test_case_name = UPPER_BOUND_NONNEG_EXTRA_BITS_1_CASE,
228+
.msb_set = false,
229+
.lower_bound = false,
230+
.extra_bits = 3,
231+
.expected = {.tv_sec = 0x37fffffffLL, .tv_nsec = 0L},
232+
}
233+
};
234+
235+
struct timespec64 timestamp;
236+
int i;
237+
238+
for (i = 0; i < ARRAY_SIZE(test_data); ++i) {
239+
timestamp.tv_sec = get_32bit_time(&test_data[i]);
240+
ext4_decode_extra_time(&timestamp,
241+
cpu_to_le32(test_data[i].extra_bits));
242+
243+
KUNIT_EXPECT_EQ_MSG(test,
244+
test_data[i].expected.tv_sec,
245+
timestamp.tv_sec,
246+
CASE_NAME_FORMAT,
247+
test_data[i].test_case_name,
248+
test_data[i].msb_set,
249+
test_data[i].lower_bound,
250+
test_data[i].extra_bits);
251+
KUNIT_EXPECT_EQ_MSG(test,
252+
test_data[i].expected.tv_nsec,
253+
timestamp.tv_nsec,
254+
CASE_NAME_FORMAT,
255+
test_data[i].test_case_name,
256+
test_data[i].msb_set,
257+
test_data[i].lower_bound,
258+
test_data[i].extra_bits);
259+
}
260+
}
261+
262+
static struct kunit_case ext4_inode_test_cases[] = {
263+
KUNIT_CASE(inode_test_xtimestamp_decoding),
264+
{}
265+
};
266+
267+
static struct kunit_suite ext4_inode_test_suite = {
268+
.name = "ext4_inode_test",
269+
.test_cases = ext4_inode_test_cases,
270+
};
271+
272+
kunit_test_suite(ext4_inode_test_suite);

0 commit comments

Comments
 (0)