Skip to content

Commit ee1d9d2

Browse files
committed
Merge: update kselftests to include new cachestat test
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/4836 JIRA: https://issues.redhat.com/browse/RHEL-50302 The syscall is already added to the tables, bring down the kselftest exercising it. Signed-off-by: Eric Chanudet <echanude@redhat.com> Approved-by: Jan Stancek <jstancek@redhat.com> Approved-by: Nico Pache <npache@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Lucas Zampieri <lzampier@redhat.com>
2 parents 4919c36 + ddce7c5 commit ee1d9d2

File tree

6 files changed

+344
-1
lines changed

6 files changed

+344
-1
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

redhat/kernel.spec.template

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2783,7 +2783,7 @@ export BPFTOOL=$(pwd)/tools/bpf/bpftool/bpftool
27832783
pushd tools/testing/selftests
27842784
# We need to install here because we need to call make with ARCH set which
27852785
# doesn't seem possible to do in the install section.
2786-
%{make} %{?_smp_mflags} ARCH=$Arch V=1 TARGETS="bpf cgroup mm livepatch net net/forwarding net/mptcp netfilter tc-testing memfd drivers/net/bonding iommu" SKIP_TARGETS="" FORCE_TARGETS=1 INSTALL_PATH=%{buildroot}%{_libexecdir}/kselftests VMLINUX_H="${RPM_VMLINUX_H}" DEFAULT_INSTALL_HDR_PATH=0 install
2786+
%{make} %{?_smp_mflags} ARCH=$Arch V=1 TARGETS="bpf cgroup mm livepatch net net/forwarding net/mptcp netfilter tc-testing memfd drivers/net/bonding iommu cachestat" SKIP_TARGETS="" FORCE_TARGETS=1 INSTALL_PATH=%{buildroot}%{_libexecdir}/kselftests VMLINUX_H="${RPM_VMLINUX_H}" DEFAULT_INSTALL_HDR_PATH=0 install
27872787

27882788
# 'make install' for bpf is broken and upstream refuses to fix it.
27892789
# Install the needed files manually.
@@ -3177,6 +3177,12 @@ find -type d -exec install -d %{buildroot}%{_libexecdir}/kselftests/iommu/{} \;
31773177
find -type f -executable -exec install -D -m755 {} %{buildroot}%{_libexecdir}/kselftests/iommu/{} \;
31783178
find -type f ! -executable -exec install -D -m644 {} %{buildroot}%{_libexecdir}/kselftests/iommu/{} \;
31793179
popd
3180+
# install cachestat selftests
3181+
pushd tools/testing/selftests/cachestat
3182+
find -type d -exec install -d %{buildroot}%{_libexecdir}/kselftests/cachestat/{} \;
3183+
find -type f -executable -exec install -D -m755 {} %{buildroot}%{_libexecdir}/kselftests/cachestat/{} \;
3184+
find -type f ! -executable -exec install -D -m644 {} %{buildroot}%{_libexecdir}/kselftests/cachestat/{} \;
3185+
popd
31803186
%endif
31813187

31823188
###

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

0 commit comments

Comments
 (0)