Skip to content

Commit 9461591

Browse files
committed
selftests: add selftests for cachestat
JIRA: https://issues.redhat.com/browse/RHEL-50302 commit 88537aa Author: Nhat Pham <nphamcs@gmail.com> Date: Tue May 2 18:36:08 2023 -0700 selftests: add selftests for cachestat Test cachestat on a newly created file, /dev/ files, /proc/ files and a directory. Also test on a shmem file (which can also be tested with huge pages since tmpfs supports huge pages). [colin.i.king@gmail.com: fix spelling mistake "trucate" -> "truncate"] Link: https://lkml.kernel.org/r/20230505110855.2493457-1-colin.i.king@gmail.com [mpe@ellerman.id.au: avoid excessive stack allocation] Link: https://lkml.kernel.org/r/877ctfa6yv.fsf@mail.lhotse Link: https://lkml.kernel.org/r/20230503013608.2431726-4-nphamcs@gmail.com Signed-off-by: Nhat Pham <nphamcs@gmail.com> Signed-off-by: Colin Ian King <colin.i.king@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Acked-by: Johannes Weiner <hannes@cmpxchg.org> Cc: Brian Foster <bfoster@redhat.com> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Cc: Colin Ian King <colin.i.king@gmail.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Eric Chanudet <echanude@redhat.com>
1 parent 0793dad commit 9461591

File tree

5 files changed

+287
-0
lines changed

5 files changed

+287
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4158,6 +4158,13 @@ S: Supported
41584158
F: Documentation/filesystems/caching/cachefiles.rst
41594159
F: fs/cachefiles/
41604160

4161+
CACHESTAT: PAGE CACHE STATS FOR A FILE
4162+
M: Nhat Pham <nphamcs@gmail.com>
4163+
M: Johannes Weiner <hannes@cmpxchg.org>
4164+
L: linux-mm@kvack.org
4165+
S: Maintained
4166+
F: tools/testing/selftests/cachestat/test_cachestat.c
4167+
41614168
CADENCE MIPI-CSI2 BRIDGES
41624169
M: Maxime Ripard <mripard@kernel.org>
41634170
L: linux-media@vger.kernel.org

tools/testing/selftests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
TARGETS = arm64
33
TARGETS += bpf
44
TARGETS += breakpoints
5+
TARGETS += cachestat
56
TARGETS += capabilities
67
TARGETS += cgroup
78
TARGETS += clone3
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
test_cachestat
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
TEST_GEN_PROGS := test_cachestat
3+
4+
CFLAGS += $(KHDR_INCLUDES)
5+
CFLAGS += -Wall
6+
CFLAGS += -lrt
7+
8+
include ../lib.mk
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#define _GNU_SOURCE
3+
4+
#include <stdio.h>
5+
#include <stdbool.h>
6+
#include <linux/kernel.h>
7+
#include <linux/mman.h>
8+
#include <sys/mman.h>
9+
#include <sys/shm.h>
10+
#include <sys/syscall.h>
11+
#include <unistd.h>
12+
#include <string.h>
13+
#include <fcntl.h>
14+
#include <errno.h>
15+
16+
#include "../kselftest.h"
17+
18+
static const char * const dev_files[] = {
19+
"/dev/zero", "/dev/null", "/dev/urandom",
20+
"/proc/version", "/proc"
21+
};
22+
static const int cachestat_nr = 451;
23+
24+
void print_cachestat(struct cachestat *cs)
25+
{
26+
ksft_print_msg(
27+
"Using cachestat: Cached: %lu, Dirty: %lu, Writeback: %lu, Evicted: %lu, Recently Evicted: %lu\n",
28+
cs->nr_cache, cs->nr_dirty, cs->nr_writeback,
29+
cs->nr_evicted, cs->nr_recently_evicted);
30+
}
31+
32+
bool write_exactly(int fd, size_t filesize)
33+
{
34+
int random_fd = open("/dev/urandom", O_RDONLY);
35+
char *cursor, *data;
36+
int remained;
37+
bool ret;
38+
39+
if (random_fd < 0) {
40+
ksft_print_msg("Unable to access urandom.\n");
41+
ret = false;
42+
goto out;
43+
}
44+
45+
data = malloc(filesize);
46+
if (!data) {
47+
ksft_print_msg("Unable to allocate data.\n");
48+
ret = false;
49+
goto close_random_fd;
50+
}
51+
52+
remained = filesize;
53+
cursor = data;
54+
55+
while (remained) {
56+
ssize_t read_len = read(random_fd, cursor, remained);
57+
58+
if (read_len <= 0) {
59+
ksft_print_msg("Unable to read from urandom.\n");
60+
ret = false;
61+
goto out_free_data;
62+
}
63+
64+
remained -= read_len;
65+
cursor += read_len;
66+
}
67+
68+
/* write random data to fd */
69+
remained = filesize;
70+
cursor = data;
71+
while (remained) {
72+
ssize_t write_len = write(fd, cursor, remained);
73+
74+
if (write_len <= 0) {
75+
ksft_print_msg("Unable write random data to file.\n");
76+
ret = false;
77+
goto out_free_data;
78+
}
79+
80+
remained -= write_len;
81+
cursor += write_len;
82+
}
83+
84+
ret = true;
85+
out_free_data:
86+
free(data);
87+
close_random_fd:
88+
close(random_fd);
89+
out:
90+
return ret;
91+
}
92+
93+
/*
94+
* Open/create the file at filename, (optionally) write random data to it
95+
* (exactly num_pages), then test the cachestat syscall on this file.
96+
*
97+
* If test_fsync == true, fsync the file, then check the number of dirty
98+
* pages.
99+
*/
100+
bool test_cachestat(const char *filename, bool write_random, bool create,
101+
bool test_fsync, unsigned long num_pages, int open_flags,
102+
mode_t open_mode)
103+
{
104+
size_t PS = sysconf(_SC_PAGESIZE);
105+
int filesize = num_pages * PS;
106+
bool ret = true;
107+
long syscall_ret;
108+
struct cachestat cs;
109+
struct cachestat_range cs_range = { 0, filesize };
110+
111+
int fd = open(filename, open_flags, open_mode);
112+
113+
if (fd == -1) {
114+
ksft_print_msg("Unable to create/open file.\n");
115+
ret = false;
116+
goto out;
117+
} else {
118+
ksft_print_msg("Create/open %s\n", filename);
119+
}
120+
121+
if (write_random) {
122+
if (!write_exactly(fd, filesize)) {
123+
ksft_print_msg("Unable to access urandom.\n");
124+
ret = false;
125+
goto out1;
126+
}
127+
}
128+
129+
syscall_ret = syscall(cachestat_nr, fd, &cs_range, &cs, 0);
130+
131+
ksft_print_msg("Cachestat call returned %ld\n", syscall_ret);
132+
133+
if (syscall_ret) {
134+
ksft_print_msg("Cachestat returned non-zero.\n");
135+
ret = false;
136+
goto out1;
137+
138+
} else {
139+
print_cachestat(&cs);
140+
141+
if (write_random) {
142+
if (cs.nr_cache + cs.nr_evicted != num_pages) {
143+
ksft_print_msg(
144+
"Total number of cached and evicted pages is off.\n");
145+
ret = false;
146+
}
147+
}
148+
}
149+
150+
if (test_fsync) {
151+
if (fsync(fd)) {
152+
ksft_print_msg("fsync fails.\n");
153+
ret = false;
154+
} else {
155+
syscall_ret = syscall(cachestat_nr, fd, &cs_range, &cs, 0);
156+
157+
ksft_print_msg("Cachestat call (after fsync) returned %ld\n",
158+
syscall_ret);
159+
160+
if (!syscall_ret) {
161+
print_cachestat(&cs);
162+
163+
if (cs.nr_dirty) {
164+
ret = false;
165+
ksft_print_msg(
166+
"Number of dirty should be zero after fsync.\n");
167+
}
168+
} else {
169+
ksft_print_msg("Cachestat (after fsync) returned non-zero.\n");
170+
ret = false;
171+
goto out1;
172+
}
173+
}
174+
}
175+
176+
out1:
177+
close(fd);
178+
179+
if (create)
180+
remove(filename);
181+
out:
182+
return ret;
183+
}
184+
185+
bool test_cachestat_shmem(void)
186+
{
187+
size_t PS = sysconf(_SC_PAGESIZE);
188+
size_t filesize = PS * 512 * 2; /* 2 2MB huge pages */
189+
int syscall_ret;
190+
size_t compute_len = PS * 512;
191+
struct cachestat_range cs_range = { PS, compute_len };
192+
char *filename = "tmpshmcstat";
193+
struct cachestat cs;
194+
bool ret = true;
195+
unsigned long num_pages = compute_len / PS;
196+
int fd = shm_open(filename, O_CREAT | O_RDWR, 0600);
197+
198+
if (fd < 0) {
199+
ksft_print_msg("Unable to create shmem file.\n");
200+
ret = false;
201+
goto out;
202+
}
203+
204+
if (ftruncate(fd, filesize)) {
205+
ksft_print_msg("Unable to truncate shmem file.\n");
206+
ret = false;
207+
goto close_fd;
208+
}
209+
210+
if (!write_exactly(fd, filesize)) {
211+
ksft_print_msg("Unable to write to shmem file.\n");
212+
ret = false;
213+
goto close_fd;
214+
}
215+
216+
syscall_ret = syscall(cachestat_nr, fd, &cs_range, &cs, 0);
217+
218+
if (syscall_ret) {
219+
ksft_print_msg("Cachestat returned non-zero.\n");
220+
ret = false;
221+
goto close_fd;
222+
} else {
223+
print_cachestat(&cs);
224+
if (cs.nr_cache + cs.nr_evicted != num_pages) {
225+
ksft_print_msg(
226+
"Total number of cached and evicted pages is off.\n");
227+
ret = false;
228+
}
229+
}
230+
231+
close_fd:
232+
shm_unlink(filename);
233+
out:
234+
return ret;
235+
}
236+
237+
int main(void)
238+
{
239+
int ret = 0;
240+
241+
for (int i = 0; i < 5; i++) {
242+
const char *dev_filename = dev_files[i];
243+
244+
if (test_cachestat(dev_filename, false, false, false,
245+
4, O_RDONLY, 0400))
246+
ksft_test_result_pass("cachestat works with %s\n", dev_filename);
247+
else {
248+
ksft_test_result_fail("cachestat fails with %s\n", dev_filename);
249+
ret = 1;
250+
}
251+
}
252+
253+
if (test_cachestat("tmpfilecachestat", true, true,
254+
true, 4, O_CREAT | O_RDWR, 0400 | 0600))
255+
ksft_test_result_pass("cachestat works with a normal file\n");
256+
else {
257+
ksft_test_result_fail("cachestat fails with normal file\n");
258+
ret = 1;
259+
}
260+
261+
if (test_cachestat_shmem())
262+
ksft_test_result_pass("cachestat works with a shmem file\n");
263+
else {
264+
ksft_test_result_fail("cachestat fails with a shmem file\n");
265+
ret = 1;
266+
}
267+
268+
return ret;
269+
}

0 commit comments

Comments
 (0)