188537aacSNhat Pham // SPDX-License-Identifier: GPL-2.0
288537aacSNhat Pham #define _GNU_SOURCE
3*91bf0634SMichael Ellerman #define __SANE_USERSPACE_TYPES__ // Use ll64
488537aacSNhat Pham
588537aacSNhat Pham #include <stdio.h>
688537aacSNhat Pham #include <stdbool.h>
788537aacSNhat Pham #include <linux/kernel.h>
8f84f62e6SAndre Przywara #include <linux/magic.h>
988537aacSNhat Pham #include <linux/mman.h>
1088537aacSNhat Pham #include <sys/mman.h>
1188537aacSNhat Pham #include <sys/shm.h>
1288537aacSNhat Pham #include <sys/syscall.h>
13f84f62e6SAndre Przywara #include <sys/vfs.h>
1488537aacSNhat Pham #include <unistd.h>
1588537aacSNhat Pham #include <string.h>
1688537aacSNhat Pham #include <fcntl.h>
1788537aacSNhat Pham #include <errno.h>
1888537aacSNhat Pham
1988537aacSNhat Pham #include "../kselftest.h"
2088537aacSNhat Pham
21f84f62e6SAndre Przywara #define NR_TESTS 9
225e56982dSAndre Przywara
2388537aacSNhat Pham static const char * const dev_files[] = {
2488537aacSNhat Pham "/dev/zero", "/dev/null", "/dev/urandom",
2588537aacSNhat Pham "/proc/version", "/proc"
2688537aacSNhat Pham };
2788537aacSNhat Pham
print_cachestat(struct cachestat * cs)2888537aacSNhat Pham void print_cachestat(struct cachestat *cs)
2988537aacSNhat Pham {
3088537aacSNhat Pham ksft_print_msg(
3188537aacSNhat Pham "Using cachestat: Cached: %lu, Dirty: %lu, Writeback: %lu, Evicted: %lu, Recently Evicted: %lu\n",
3288537aacSNhat Pham cs->nr_cache, cs->nr_dirty, cs->nr_writeback,
3388537aacSNhat Pham cs->nr_evicted, cs->nr_recently_evicted);
3488537aacSNhat Pham }
3588537aacSNhat Pham
write_exactly(int fd,size_t filesize)3688537aacSNhat Pham bool write_exactly(int fd, size_t filesize)
3788537aacSNhat Pham {
3888537aacSNhat Pham int random_fd = open("/dev/urandom", O_RDONLY);
3988537aacSNhat Pham char *cursor, *data;
4088537aacSNhat Pham int remained;
4188537aacSNhat Pham bool ret;
4288537aacSNhat Pham
4388537aacSNhat Pham if (random_fd < 0) {
4488537aacSNhat Pham ksft_print_msg("Unable to access urandom.\n");
4588537aacSNhat Pham ret = false;
4688537aacSNhat Pham goto out;
4788537aacSNhat Pham }
4888537aacSNhat Pham
4988537aacSNhat Pham data = malloc(filesize);
5088537aacSNhat Pham if (!data) {
5188537aacSNhat Pham ksft_print_msg("Unable to allocate data.\n");
5288537aacSNhat Pham ret = false;
5388537aacSNhat Pham goto close_random_fd;
5488537aacSNhat Pham }
5588537aacSNhat Pham
5688537aacSNhat Pham remained = filesize;
5788537aacSNhat Pham cursor = data;
5888537aacSNhat Pham
5988537aacSNhat Pham while (remained) {
6088537aacSNhat Pham ssize_t read_len = read(random_fd, cursor, remained);
6188537aacSNhat Pham
6288537aacSNhat Pham if (read_len <= 0) {
6388537aacSNhat Pham ksft_print_msg("Unable to read from urandom.\n");
6488537aacSNhat Pham ret = false;
6588537aacSNhat Pham goto out_free_data;
6688537aacSNhat Pham }
6788537aacSNhat Pham
6888537aacSNhat Pham remained -= read_len;
6988537aacSNhat Pham cursor += read_len;
7088537aacSNhat Pham }
7188537aacSNhat Pham
7288537aacSNhat Pham /* write random data to fd */
7388537aacSNhat Pham remained = filesize;
7488537aacSNhat Pham cursor = data;
7588537aacSNhat Pham while (remained) {
7688537aacSNhat Pham ssize_t write_len = write(fd, cursor, remained);
7788537aacSNhat Pham
7888537aacSNhat Pham if (write_len <= 0) {
7988537aacSNhat Pham ksft_print_msg("Unable write random data to file.\n");
8088537aacSNhat Pham ret = false;
8188537aacSNhat Pham goto out_free_data;
8288537aacSNhat Pham }
8388537aacSNhat Pham
8488537aacSNhat Pham remained -= write_len;
8588537aacSNhat Pham cursor += write_len;
8688537aacSNhat Pham }
8788537aacSNhat Pham
8888537aacSNhat Pham ret = true;
8988537aacSNhat Pham out_free_data:
9088537aacSNhat Pham free(data);
9188537aacSNhat Pham close_random_fd:
9288537aacSNhat Pham close(random_fd);
9388537aacSNhat Pham out:
9488537aacSNhat Pham return ret;
9588537aacSNhat Pham }
9688537aacSNhat Pham
9788537aacSNhat Pham /*
98f84f62e6SAndre Przywara * fsync() is implemented via noop_fsync() on tmpfs. This makes the fsync()
99f84f62e6SAndre Przywara * test fail below, so we need to check for test file living on a tmpfs.
100f84f62e6SAndre Przywara */
is_on_tmpfs(int fd)101f84f62e6SAndre Przywara static bool is_on_tmpfs(int fd)
102f84f62e6SAndre Przywara {
103f84f62e6SAndre Przywara struct statfs statfs_buf;
104f84f62e6SAndre Przywara
105f84f62e6SAndre Przywara if (fstatfs(fd, &statfs_buf))
106f84f62e6SAndre Przywara return false;
107f84f62e6SAndre Przywara
108f84f62e6SAndre Przywara return statfs_buf.f_type == TMPFS_MAGIC;
109f84f62e6SAndre Przywara }
110f84f62e6SAndre Przywara
111f84f62e6SAndre Przywara /*
11288537aacSNhat Pham * Open/create the file at filename, (optionally) write random data to it
11388537aacSNhat Pham * (exactly num_pages), then test the cachestat syscall on this file.
11488537aacSNhat Pham *
11588537aacSNhat Pham * If test_fsync == true, fsync the file, then check the number of dirty
11688537aacSNhat Pham * pages.
11788537aacSNhat Pham */
test_cachestat(const char * filename,bool write_random,bool create,bool test_fsync,unsigned long num_pages,int open_flags,mode_t open_mode)118f84f62e6SAndre Przywara static int test_cachestat(const char *filename, bool write_random, bool create,
119f84f62e6SAndre Przywara bool test_fsync, unsigned long num_pages,
120f84f62e6SAndre Przywara int open_flags, mode_t open_mode)
12188537aacSNhat Pham {
12288537aacSNhat Pham size_t PS = sysconf(_SC_PAGESIZE);
12388537aacSNhat Pham int filesize = num_pages * PS;
124f84f62e6SAndre Przywara int ret = KSFT_PASS;
12588537aacSNhat Pham long syscall_ret;
12688537aacSNhat Pham struct cachestat cs;
12788537aacSNhat Pham struct cachestat_range cs_range = { 0, filesize };
12888537aacSNhat Pham
12988537aacSNhat Pham int fd = open(filename, open_flags, open_mode);
13088537aacSNhat Pham
13188537aacSNhat Pham if (fd == -1) {
13288537aacSNhat Pham ksft_print_msg("Unable to create/open file.\n");
133f84f62e6SAndre Przywara ret = KSFT_FAIL;
13488537aacSNhat Pham goto out;
13588537aacSNhat Pham } else {
13688537aacSNhat Pham ksft_print_msg("Create/open %s\n", filename);
13788537aacSNhat Pham }
13888537aacSNhat Pham
13988537aacSNhat Pham if (write_random) {
14088537aacSNhat Pham if (!write_exactly(fd, filesize)) {
14188537aacSNhat Pham ksft_print_msg("Unable to access urandom.\n");
142f84f62e6SAndre Przywara ret = KSFT_FAIL;
14388537aacSNhat Pham goto out1;
14488537aacSNhat Pham }
14588537aacSNhat Pham }
14688537aacSNhat Pham
1479b1db732SAndre Przywara syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
14888537aacSNhat Pham
14988537aacSNhat Pham ksft_print_msg("Cachestat call returned %ld\n", syscall_ret);
15088537aacSNhat Pham
15188537aacSNhat Pham if (syscall_ret) {
15288537aacSNhat Pham ksft_print_msg("Cachestat returned non-zero.\n");
153f84f62e6SAndre Przywara ret = KSFT_FAIL;
15488537aacSNhat Pham goto out1;
15588537aacSNhat Pham
15688537aacSNhat Pham } else {
15788537aacSNhat Pham print_cachestat(&cs);
15888537aacSNhat Pham
15988537aacSNhat Pham if (write_random) {
16088537aacSNhat Pham if (cs.nr_cache + cs.nr_evicted != num_pages) {
16188537aacSNhat Pham ksft_print_msg(
16288537aacSNhat Pham "Total number of cached and evicted pages is off.\n");
163f84f62e6SAndre Przywara ret = KSFT_FAIL;
16488537aacSNhat Pham }
16588537aacSNhat Pham }
16688537aacSNhat Pham }
16788537aacSNhat Pham
16888537aacSNhat Pham if (test_fsync) {
169f84f62e6SAndre Przywara if (is_on_tmpfs(fd)) {
170f84f62e6SAndre Przywara ret = KSFT_SKIP;
171f84f62e6SAndre Przywara } else if (fsync(fd)) {
17288537aacSNhat Pham ksft_print_msg("fsync fails.\n");
173f84f62e6SAndre Przywara ret = KSFT_FAIL;
17488537aacSNhat Pham } else {
1759b1db732SAndre Przywara syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
17688537aacSNhat Pham
17788537aacSNhat Pham ksft_print_msg("Cachestat call (after fsync) returned %ld\n",
17888537aacSNhat Pham syscall_ret);
17988537aacSNhat Pham
18088537aacSNhat Pham if (!syscall_ret) {
18188537aacSNhat Pham print_cachestat(&cs);
18288537aacSNhat Pham
18388537aacSNhat Pham if (cs.nr_dirty) {
184f84f62e6SAndre Przywara ret = KSFT_FAIL;
18588537aacSNhat Pham ksft_print_msg(
18688537aacSNhat Pham "Number of dirty should be zero after fsync.\n");
18788537aacSNhat Pham }
18888537aacSNhat Pham } else {
18988537aacSNhat Pham ksft_print_msg("Cachestat (after fsync) returned non-zero.\n");
190f84f62e6SAndre Przywara ret = KSFT_FAIL;
19188537aacSNhat Pham goto out1;
19288537aacSNhat Pham }
19388537aacSNhat Pham }
19488537aacSNhat Pham }
19588537aacSNhat Pham
19688537aacSNhat Pham out1:
19788537aacSNhat Pham close(fd);
19888537aacSNhat Pham
19988537aacSNhat Pham if (create)
20088537aacSNhat Pham remove(filename);
20188537aacSNhat Pham out:
20288537aacSNhat Pham return ret;
20388537aacSNhat Pham }
20488537aacSNhat Pham
test_cachestat_shmem(void)20588537aacSNhat Pham bool test_cachestat_shmem(void)
20688537aacSNhat Pham {
20788537aacSNhat Pham size_t PS = sysconf(_SC_PAGESIZE);
20888537aacSNhat Pham size_t filesize = PS * 512 * 2; /* 2 2MB huge pages */
20988537aacSNhat Pham int syscall_ret;
21088537aacSNhat Pham size_t compute_len = PS * 512;
21188537aacSNhat Pham struct cachestat_range cs_range = { PS, compute_len };
21288537aacSNhat Pham char *filename = "tmpshmcstat";
21388537aacSNhat Pham struct cachestat cs;
21488537aacSNhat Pham bool ret = true;
21588537aacSNhat Pham unsigned long num_pages = compute_len / PS;
21688537aacSNhat Pham int fd = shm_open(filename, O_CREAT | O_RDWR, 0600);
21788537aacSNhat Pham
21888537aacSNhat Pham if (fd < 0) {
21988537aacSNhat Pham ksft_print_msg("Unable to create shmem file.\n");
22088537aacSNhat Pham ret = false;
22188537aacSNhat Pham goto out;
22288537aacSNhat Pham }
22388537aacSNhat Pham
22488537aacSNhat Pham if (ftruncate(fd, filesize)) {
22588537aacSNhat Pham ksft_print_msg("Unable to truncate shmem file.\n");
22688537aacSNhat Pham ret = false;
22788537aacSNhat Pham goto close_fd;
22888537aacSNhat Pham }
22988537aacSNhat Pham
23088537aacSNhat Pham if (!write_exactly(fd, filesize)) {
23188537aacSNhat Pham ksft_print_msg("Unable to write to shmem file.\n");
23288537aacSNhat Pham ret = false;
23388537aacSNhat Pham goto close_fd;
23488537aacSNhat Pham }
23588537aacSNhat Pham
2369b1db732SAndre Przywara syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
23788537aacSNhat Pham
23888537aacSNhat Pham if (syscall_ret) {
23988537aacSNhat Pham ksft_print_msg("Cachestat returned non-zero.\n");
24088537aacSNhat Pham ret = false;
24188537aacSNhat Pham goto close_fd;
24288537aacSNhat Pham } else {
24388537aacSNhat Pham print_cachestat(&cs);
24488537aacSNhat Pham if (cs.nr_cache + cs.nr_evicted != num_pages) {
24588537aacSNhat Pham ksft_print_msg(
24688537aacSNhat Pham "Total number of cached and evicted pages is off.\n");
24788537aacSNhat Pham ret = false;
24888537aacSNhat Pham }
24988537aacSNhat Pham }
25088537aacSNhat Pham
25188537aacSNhat Pham close_fd:
25288537aacSNhat Pham shm_unlink(filename);
25388537aacSNhat Pham out:
25488537aacSNhat Pham return ret;
25588537aacSNhat Pham }
25688537aacSNhat Pham
main(void)25788537aacSNhat Pham int main(void)
25888537aacSNhat Pham {
2595e56982dSAndre Przywara int ret;
2605e56982dSAndre Przywara
2615e56982dSAndre Przywara ksft_print_header();
2625e56982dSAndre Przywara
2635e56982dSAndre Przywara ret = syscall(__NR_cachestat, -1, NULL, NULL, 0);
2645e56982dSAndre Przywara if (ret == -1 && errno == ENOSYS)
2655e56982dSAndre Przywara ksft_exit_skip("cachestat syscall not available\n");
2665e56982dSAndre Przywara
2675e56982dSAndre Przywara ksft_set_plan(NR_TESTS);
2685e56982dSAndre Przywara
2695e56982dSAndre Przywara if (ret == -1 && errno == EBADF) {
2705e56982dSAndre Przywara ksft_test_result_pass("bad file descriptor recognized\n");
2715e56982dSAndre Przywara ret = 0;
2725e56982dSAndre Przywara } else {
2735e56982dSAndre Przywara ksft_test_result_fail("bad file descriptor ignored\n");
2745e56982dSAndre Przywara ret = 1;
2755e56982dSAndre Przywara }
27688537aacSNhat Pham
27788537aacSNhat Pham for (int i = 0; i < 5; i++) {
27888537aacSNhat Pham const char *dev_filename = dev_files[i];
27988537aacSNhat Pham
28088537aacSNhat Pham if (test_cachestat(dev_filename, false, false, false,
281f84f62e6SAndre Przywara 4, O_RDONLY, 0400) == KSFT_PASS)
28288537aacSNhat Pham ksft_test_result_pass("cachestat works with %s\n", dev_filename);
28388537aacSNhat Pham else {
28488537aacSNhat Pham ksft_test_result_fail("cachestat fails with %s\n", dev_filename);
28588537aacSNhat Pham ret = 1;
28688537aacSNhat Pham }
28788537aacSNhat Pham }
28888537aacSNhat Pham
28988537aacSNhat Pham if (test_cachestat("tmpfilecachestat", true, true,
290f84f62e6SAndre Przywara false, 4, O_CREAT | O_RDWR, 0600) == KSFT_PASS)
29188537aacSNhat Pham ksft_test_result_pass("cachestat works with a normal file\n");
29288537aacSNhat Pham else {
29388537aacSNhat Pham ksft_test_result_fail("cachestat fails with normal file\n");
29488537aacSNhat Pham ret = 1;
29588537aacSNhat Pham }
29688537aacSNhat Pham
297f84f62e6SAndre Przywara switch (test_cachestat("tmpfilecachestat", true, true,
298f84f62e6SAndre Przywara true, 4, O_CREAT | O_RDWR, 0600)) {
299f84f62e6SAndre Przywara case KSFT_FAIL:
300f84f62e6SAndre Przywara ksft_test_result_fail("cachestat fsync fails with normal file\n");
301f84f62e6SAndre Przywara ret = KSFT_FAIL;
302f84f62e6SAndre Przywara break;
303f84f62e6SAndre Przywara case KSFT_PASS:
304f84f62e6SAndre Przywara ksft_test_result_pass("cachestat fsync works with a normal file\n");
305f84f62e6SAndre Przywara break;
306f84f62e6SAndre Przywara case KSFT_SKIP:
307f84f62e6SAndre Przywara ksft_test_result_skip("tmpfilecachestat is on tmpfs\n");
308f84f62e6SAndre Przywara break;
309f84f62e6SAndre Przywara }
310f84f62e6SAndre Przywara
31188537aacSNhat Pham if (test_cachestat_shmem())
31288537aacSNhat Pham ksft_test_result_pass("cachestat works with a shmem file\n");
31388537aacSNhat Pham else {
31488537aacSNhat Pham ksft_test_result_fail("cachestat fails with a shmem file\n");
31588537aacSNhat Pham ret = 1;
31688537aacSNhat Pham }
31788537aacSNhat Pham
31888537aacSNhat Pham return ret;
31988537aacSNhat Pham }
320