Skip to content

Commit f84f62e

Browse files
Andre-ARMakpm00
authored andcommitted
selftests: cachestat: catch failing fsync test on tmpfs
The cachestat kselftest runs a test on a normal file, which is created temporarily in the current directory. Among the tests it runs there is a call to fsync(), which is expected to clean all dirty pages used by the file. However the tmpfs filesystem implements fsync() as noop_fsync(), so the call will not even attempt to clean anything when this test file happens to live on a tmpfs instance. This happens in an initramfs, or when the current directory is in /dev/shm or sometimes /tmp. To avoid this test failing wrongly, use statfs() to check which filesystem the test file lives on. If that is "tmpfs", we skip the fsync() test. Since the fsync test is only one part of the "normal file" test, we now execute this twice, skipping the fsync part on the first call. This way only the second test, including the fsync part, would be skipped. Link: https://lkml.kernel.org/r/20230821160534.3414911-3-andre.przywara@arm.com Signed-off-by: Andre Przywara <andre.przywara@arm.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Nhat Pham <nphamcs@gmail.com> Cc: Shuah Khan <shuah@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 5e56982 commit f84f62e

File tree

1 file changed

+47
-15
lines changed

1 file changed

+47
-15
lines changed

tools/testing/selftests/cachestat/test_cachestat.c

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@
44
#include <stdio.h>
55
#include <stdbool.h>
66
#include <linux/kernel.h>
7+
#include <linux/magic.h>
78
#include <linux/mman.h>
89
#include <sys/mman.h>
910
#include <sys/shm.h>
1011
#include <sys/syscall.h>
12+
#include <sys/vfs.h>
1113
#include <unistd.h>
1214
#include <string.h>
1315
#include <fcntl.h>
1416
#include <errno.h>
1517

1618
#include "../kselftest.h"
1719

18-
#define NR_TESTS 8
20+
#define NR_TESTS 9
1921

2022
static const char * const dev_files[] = {
2123
"/dev/zero", "/dev/null", "/dev/urandom",
@@ -92,20 +94,34 @@ bool write_exactly(int fd, size_t filesize)
9294
return ret;
9395
}
9496

97+
/*
98+
* fsync() is implemented via noop_fsync() on tmpfs. This makes the fsync()
99+
* test fail below, so we need to check for test file living on a tmpfs.
100+
*/
101+
static bool is_on_tmpfs(int fd)
102+
{
103+
struct statfs statfs_buf;
104+
105+
if (fstatfs(fd, &statfs_buf))
106+
return false;
107+
108+
return statfs_buf.f_type == TMPFS_MAGIC;
109+
}
110+
95111
/*
96112
* Open/create the file at filename, (optionally) write random data to it
97113
* (exactly num_pages), then test the cachestat syscall on this file.
98114
*
99115
* If test_fsync == true, fsync the file, then check the number of dirty
100116
* pages.
101117
*/
102-
bool test_cachestat(const char *filename, bool write_random, bool create,
103-
bool test_fsync, unsigned long num_pages, int open_flags,
104-
mode_t open_mode)
118+
static int test_cachestat(const char *filename, bool write_random, bool create,
119+
bool test_fsync, unsigned long num_pages,
120+
int open_flags, mode_t open_mode)
105121
{
106122
size_t PS = sysconf(_SC_PAGESIZE);
107123
int filesize = num_pages * PS;
108-
bool ret = true;
124+
int ret = KSFT_PASS;
109125
long syscall_ret;
110126
struct cachestat cs;
111127
struct cachestat_range cs_range = { 0, filesize };
@@ -114,7 +130,7 @@ bool test_cachestat(const char *filename, bool write_random, bool create,
114130

115131
if (fd == -1) {
116132
ksft_print_msg("Unable to create/open file.\n");
117-
ret = false;
133+
ret = KSFT_FAIL;
118134
goto out;
119135
} else {
120136
ksft_print_msg("Create/open %s\n", filename);
@@ -123,7 +139,7 @@ bool test_cachestat(const char *filename, bool write_random, bool create,
123139
if (write_random) {
124140
if (!write_exactly(fd, filesize)) {
125141
ksft_print_msg("Unable to access urandom.\n");
126-
ret = false;
142+
ret = KSFT_FAIL;
127143
goto out1;
128144
}
129145
}
@@ -134,7 +150,7 @@ bool test_cachestat(const char *filename, bool write_random, bool create,
134150

135151
if (syscall_ret) {
136152
ksft_print_msg("Cachestat returned non-zero.\n");
137-
ret = false;
153+
ret = KSFT_FAIL;
138154
goto out1;
139155

140156
} else {
@@ -144,15 +160,17 @@ bool test_cachestat(const char *filename, bool write_random, bool create,
144160
if (cs.nr_cache + cs.nr_evicted != num_pages) {
145161
ksft_print_msg(
146162
"Total number of cached and evicted pages is off.\n");
147-
ret = false;
163+
ret = KSFT_FAIL;
148164
}
149165
}
150166
}
151167

152168
if (test_fsync) {
153-
if (fsync(fd)) {
169+
if (is_on_tmpfs(fd)) {
170+
ret = KSFT_SKIP;
171+
} else if (fsync(fd)) {
154172
ksft_print_msg("fsync fails.\n");
155-
ret = false;
173+
ret = KSFT_FAIL;
156174
} else {
157175
syscall_ret = syscall(cachestat_nr, fd, &cs_range, &cs, 0);
158176

@@ -163,13 +181,13 @@ bool test_cachestat(const char *filename, bool write_random, bool create,
163181
print_cachestat(&cs);
164182

165183
if (cs.nr_dirty) {
166-
ret = false;
184+
ret = KSFT_FAIL;
167185
ksft_print_msg(
168186
"Number of dirty should be zero after fsync.\n");
169187
}
170188
} else {
171189
ksft_print_msg("Cachestat (after fsync) returned non-zero.\n");
172-
ret = false;
190+
ret = KSFT_FAIL;
173191
goto out1;
174192
}
175193
}
@@ -260,7 +278,7 @@ int main(void)
260278
const char *dev_filename = dev_files[i];
261279

262280
if (test_cachestat(dev_filename, false, false, false,
263-
4, O_RDONLY, 0400))
281+
4, O_RDONLY, 0400) == KSFT_PASS)
264282
ksft_test_result_pass("cachestat works with %s\n", dev_filename);
265283
else {
266284
ksft_test_result_fail("cachestat fails with %s\n", dev_filename);
@@ -269,13 +287,27 @@ int main(void)
269287
}
270288

271289
if (test_cachestat("tmpfilecachestat", true, true,
272-
true, 4, O_CREAT | O_RDWR, 0400 | 0600))
290+
false, 4, O_CREAT | O_RDWR, 0600) == KSFT_PASS)
273291
ksft_test_result_pass("cachestat works with a normal file\n");
274292
else {
275293
ksft_test_result_fail("cachestat fails with normal file\n");
276294
ret = 1;
277295
}
278296

297+
switch (test_cachestat("tmpfilecachestat", true, true,
298+
true, 4, O_CREAT | O_RDWR, 0600)) {
299+
case KSFT_FAIL:
300+
ksft_test_result_fail("cachestat fsync fails with normal file\n");
301+
ret = KSFT_FAIL;
302+
break;
303+
case KSFT_PASS:
304+
ksft_test_result_pass("cachestat fsync works with a normal file\n");
305+
break;
306+
case KSFT_SKIP:
307+
ksft_test_result_skip("tmpfilecachestat is on tmpfs\n");
308+
break;
309+
}
310+
279311
if (test_cachestat_shmem())
280312
ksft_test_result_pass("cachestat works with a shmem file\n");
281313
else {

0 commit comments

Comments
 (0)