2023-05-02 18:36:08 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#define _GNU_SOURCE
|
2024-05-21 13:01:11 +10:00
|
|
|
#define __SANE_USERSPACE_TYPES__ // Use ll64
|
2023-05-02 18:36:08 -07:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <linux/kernel.h>
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
#include <linux/magic.h>
|
2023-05-02 18:36:08 -07:00
|
|
|
#include <linux/mman.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#include <sys/syscall.h>
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
#include <sys/vfs.h>
|
2023-05-02 18:36:08 -07:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "../kselftest.h"
|
|
|
|
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
#define NR_TESTS 9
|
2023-08-21 17:05:33 +01:00
|
|
|
|
2023-05-02 18:36:08 -07:00
|
|
|
static const char * const dev_files[] = {
|
|
|
|
"/dev/zero", "/dev/null", "/dev/urandom",
|
|
|
|
"/proc/version", "/proc"
|
|
|
|
};
|
|
|
|
|
|
|
|
void print_cachestat(struct cachestat *cs)
|
|
|
|
{
|
|
|
|
ksft_print_msg(
|
2023-10-13 13:36:26 +02:00
|
|
|
"Using cachestat: Cached: %llu, Dirty: %llu, Writeback: %llu, Evicted: %llu, Recently Evicted: %llu\n",
|
2023-05-02 18:36:08 -07:00
|
|
|
cs->nr_cache, cs->nr_dirty, cs->nr_writeback,
|
|
|
|
cs->nr_evicted, cs->nr_recently_evicted);
|
|
|
|
}
|
|
|
|
|
2025-07-09 23:16:57 +05:30
|
|
|
enum file_type {
|
|
|
|
FILE_MMAP,
|
|
|
|
FILE_SHMEM
|
|
|
|
};
|
|
|
|
|
2023-05-02 18:36:08 -07:00
|
|
|
bool write_exactly(int fd, size_t filesize)
|
|
|
|
{
|
|
|
|
int random_fd = open("/dev/urandom", O_RDONLY);
|
|
|
|
char *cursor, *data;
|
|
|
|
int remained;
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
if (random_fd < 0) {
|
|
|
|
ksft_print_msg("Unable to access urandom.\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = malloc(filesize);
|
|
|
|
if (!data) {
|
|
|
|
ksft_print_msg("Unable to allocate data.\n");
|
|
|
|
ret = false;
|
|
|
|
goto close_random_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
remained = filesize;
|
|
|
|
cursor = data;
|
|
|
|
|
|
|
|
while (remained) {
|
|
|
|
ssize_t read_len = read(random_fd, cursor, remained);
|
|
|
|
|
|
|
|
if (read_len <= 0) {
|
|
|
|
ksft_print_msg("Unable to read from urandom.\n");
|
|
|
|
ret = false;
|
|
|
|
goto out_free_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
remained -= read_len;
|
|
|
|
cursor += read_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write random data to fd */
|
|
|
|
remained = filesize;
|
|
|
|
cursor = data;
|
|
|
|
while (remained) {
|
|
|
|
ssize_t write_len = write(fd, cursor, remained);
|
|
|
|
|
|
|
|
if (write_len <= 0) {
|
|
|
|
ksft_print_msg("Unable write random data to file.\n");
|
|
|
|
ret = false;
|
|
|
|
goto out_free_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
remained -= write_len;
|
|
|
|
cursor += write_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
out_free_data:
|
|
|
|
free(data);
|
|
|
|
close_random_fd:
|
|
|
|
close(random_fd);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
/*
|
|
|
|
* fsync() is implemented via noop_fsync() on tmpfs. This makes the fsync()
|
|
|
|
* test fail below, so we need to check for test file living on a tmpfs.
|
|
|
|
*/
|
|
|
|
static bool is_on_tmpfs(int fd)
|
|
|
|
{
|
|
|
|
struct statfs statfs_buf;
|
|
|
|
|
|
|
|
if (fstatfs(fd, &statfs_buf))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return statfs_buf.f_type == TMPFS_MAGIC;
|
|
|
|
}
|
|
|
|
|
2023-05-02 18:36:08 -07:00
|
|
|
/*
|
|
|
|
* Open/create the file at filename, (optionally) write random data to it
|
|
|
|
* (exactly num_pages), then test the cachestat syscall on this file.
|
|
|
|
*
|
|
|
|
* If test_fsync == true, fsync the file, then check the number of dirty
|
|
|
|
* pages.
|
|
|
|
*/
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
static int test_cachestat(const char *filename, bool write_random, bool create,
|
|
|
|
bool test_fsync, unsigned long num_pages,
|
|
|
|
int open_flags, mode_t open_mode)
|
2023-05-02 18:36:08 -07:00
|
|
|
{
|
|
|
|
size_t PS = sysconf(_SC_PAGESIZE);
|
|
|
|
int filesize = num_pages * PS;
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
int ret = KSFT_PASS;
|
2023-05-02 18:36:08 -07:00
|
|
|
long syscall_ret;
|
|
|
|
struct cachestat cs;
|
|
|
|
struct cachestat_range cs_range = { 0, filesize };
|
|
|
|
|
|
|
|
int fd = open(filename, open_flags, open_mode);
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
ksft_print_msg("Unable to create/open file.\n");
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
ksft_print_msg("Create/open %s\n", filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (write_random) {
|
|
|
|
if (!write_exactly(fd, filesize)) {
|
|
|
|
ksft_print_msg("Unable to access urandom.\n");
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-15 16:56:11 +01:00
|
|
|
syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
|
2023-05-02 18:36:08 -07:00
|
|
|
|
|
|
|
ksft_print_msg("Cachestat call returned %ld\n", syscall_ret);
|
|
|
|
|
|
|
|
if (syscall_ret) {
|
|
|
|
ksft_print_msg("Cachestat returned non-zero.\n");
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
goto out1;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
print_cachestat(&cs);
|
|
|
|
|
|
|
|
if (write_random) {
|
|
|
|
if (cs.nr_cache + cs.nr_evicted != num_pages) {
|
|
|
|
ksft_print_msg(
|
|
|
|
"Total number of cached and evicted pages is off.\n");
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_fsync) {
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
if (is_on_tmpfs(fd)) {
|
|
|
|
ret = KSFT_SKIP;
|
|
|
|
} else if (fsync(fd)) {
|
2023-05-02 18:36:08 -07:00
|
|
|
ksft_print_msg("fsync fails.\n");
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
} else {
|
2023-08-15 16:56:11 +01:00
|
|
|
syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
|
2023-05-02 18:36:08 -07:00
|
|
|
|
|
|
|
ksft_print_msg("Cachestat call (after fsync) returned %ld\n",
|
|
|
|
syscall_ret);
|
|
|
|
|
|
|
|
if (!syscall_ret) {
|
|
|
|
print_cachestat(&cs);
|
|
|
|
|
|
|
|
if (cs.nr_dirty) {
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
ksft_print_msg(
|
|
|
|
"Number of dirty should be zero after fsync.\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ksft_print_msg("Cachestat (after fsync) returned non-zero.\n");
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
ret = KSFT_FAIL;
|
2023-05-02 18:36:08 -07:00
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out1:
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (create)
|
|
|
|
remove(filename);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2025-07-09 23:16:57 +05:30
|
|
|
const char *file_type_str(enum file_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case FILE_SHMEM:
|
|
|
|
return "shmem";
|
|
|
|
case FILE_MMAP:
|
|
|
|
return "mmap";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
2023-05-02 18:36:08 -07:00
|
|
|
|
2025-07-09 23:16:57 +05:30
|
|
|
|
|
|
|
bool run_cachestat_test(enum file_type type)
|
2023-05-02 18:36:08 -07:00
|
|
|
{
|
|
|
|
size_t PS = sysconf(_SC_PAGESIZE);
|
|
|
|
size_t filesize = PS * 512 * 2; /* 2 2MB huge pages */
|
|
|
|
int syscall_ret;
|
|
|
|
size_t compute_len = PS * 512;
|
|
|
|
struct cachestat_range cs_range = { PS, compute_len };
|
|
|
|
char *filename = "tmpshmcstat";
|
|
|
|
struct cachestat cs;
|
|
|
|
bool ret = true;
|
2025-07-09 23:16:57 +05:30
|
|
|
int fd;
|
2023-05-02 18:36:08 -07:00
|
|
|
unsigned long num_pages = compute_len / PS;
|
2025-07-09 23:16:57 +05:30
|
|
|
if (type == FILE_SHMEM)
|
|
|
|
fd = shm_open(filename, O_CREAT | O_RDWR, 0600);
|
|
|
|
else
|
|
|
|
fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
2023-05-02 18:36:08 -07:00
|
|
|
|
|
|
|
if (fd < 0) {
|
2025-07-09 23:16:57 +05:30
|
|
|
ksft_print_msg("Unable to create %s file.\n",
|
|
|
|
file_type_str(type));
|
2023-05-02 18:36:08 -07:00
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ftruncate(fd, filesize)) {
|
2025-07-09 23:16:57 +05:30
|
|
|
ksft_print_msg("Unable to truncate %s file.\n",file_type_str(type));
|
2023-05-02 18:36:08 -07:00
|
|
|
ret = false;
|
|
|
|
goto close_fd;
|
|
|
|
}
|
2025-07-09 23:16:57 +05:30
|
|
|
switch (type) {
|
|
|
|
case FILE_SHMEM:
|
|
|
|
if (!write_exactly(fd, filesize)) {
|
|
|
|
ksft_print_msg("Unable to write to file.\n");
|
|
|
|
ret = false;
|
|
|
|
goto close_fd;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FILE_MMAP:
|
|
|
|
char *map = mmap(NULL, filesize, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_SHARED, fd, 0);
|
2023-05-02 18:36:08 -07:00
|
|
|
|
2025-07-09 23:16:57 +05:30
|
|
|
if (map == MAP_FAILED) {
|
|
|
|
ksft_print_msg("mmap failed.\n");
|
|
|
|
ret = false;
|
|
|
|
goto close_fd;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < filesize; i++)
|
|
|
|
map[i] = 'A';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ksft_print_msg("Unsupported file type.\n");
|
2023-05-02 18:36:08 -07:00
|
|
|
ret = false;
|
|
|
|
goto close_fd;
|
|
|
|
}
|
2023-08-15 16:56:11 +01:00
|
|
|
syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
|
2023-05-02 18:36:08 -07:00
|
|
|
|
|
|
|
if (syscall_ret) {
|
|
|
|
ksft_print_msg("Cachestat returned non-zero.\n");
|
|
|
|
ret = false;
|
|
|
|
goto close_fd;
|
|
|
|
} else {
|
|
|
|
print_cachestat(&cs);
|
|
|
|
if (cs.nr_cache + cs.nr_evicted != num_pages) {
|
|
|
|
ksft_print_msg(
|
|
|
|
"Total number of cached and evicted pages is off.\n");
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close_fd:
|
|
|
|
shm_unlink(filename);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
{
|
2023-08-21 17:05:33 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ksft_print_header();
|
|
|
|
|
|
|
|
ret = syscall(__NR_cachestat, -1, NULL, NULL, 0);
|
|
|
|
if (ret == -1 && errno == ENOSYS)
|
|
|
|
ksft_exit_skip("cachestat syscall not available\n");
|
|
|
|
|
|
|
|
ksft_set_plan(NR_TESTS);
|
|
|
|
|
|
|
|
if (ret == -1 && errno == EBADF) {
|
|
|
|
ksft_test_result_pass("bad file descriptor recognized\n");
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ksft_test_result_fail("bad file descriptor ignored\n");
|
|
|
|
ret = 1;
|
|
|
|
}
|
2023-05-02 18:36:08 -07:00
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
const char *dev_filename = dev_files[i];
|
|
|
|
|
|
|
|
if (test_cachestat(dev_filename, false, false, false,
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
4, O_RDONLY, 0400) == KSFT_PASS)
|
2023-05-02 18:36:08 -07:00
|
|
|
ksft_test_result_pass("cachestat works with %s\n", dev_filename);
|
|
|
|
else {
|
|
|
|
ksft_test_result_fail("cachestat fails with %s\n", dev_filename);
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_cachestat("tmpfilecachestat", true, true,
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
false, 4, O_CREAT | O_RDWR, 0600) == KSFT_PASS)
|
2023-05-02 18:36:08 -07:00
|
|
|
ksft_test_result_pass("cachestat works with a normal file\n");
|
|
|
|
else {
|
|
|
|
ksft_test_result_fail("cachestat fails with normal file\n");
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
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>
2023-08-21 17:05:34 +01:00
|
|
|
switch (test_cachestat("tmpfilecachestat", true, true,
|
|
|
|
true, 4, O_CREAT | O_RDWR, 0600)) {
|
|
|
|
case KSFT_FAIL:
|
|
|
|
ksft_test_result_fail("cachestat fsync fails with normal file\n");
|
|
|
|
ret = KSFT_FAIL;
|
|
|
|
break;
|
|
|
|
case KSFT_PASS:
|
|
|
|
ksft_test_result_pass("cachestat fsync works with a normal file\n");
|
|
|
|
break;
|
|
|
|
case KSFT_SKIP:
|
|
|
|
ksft_test_result_skip("tmpfilecachestat is on tmpfs\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2025-07-09 23:16:57 +05:30
|
|
|
if (run_cachestat_test(FILE_SHMEM))
|
2023-05-02 18:36:08 -07:00
|
|
|
ksft_test_result_pass("cachestat works with a shmem file\n");
|
|
|
|
else {
|
|
|
|
ksft_test_result_fail("cachestat fails with a shmem file\n");
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
2025-07-09 23:16:57 +05:30
|
|
|
if (run_cachestat_test(FILE_MMAP))
|
|
|
|
ksft_test_result_pass("cachestat works with a mmap file\n");
|
|
|
|
else {
|
|
|
|
ksft_test_result_fail("cachestat fails with a mmap file\n");
|
|
|
|
ret = 1;
|
|
|
|
}
|
2023-05-02 18:36:08 -07:00
|
|
|
return ret;
|
|
|
|
}
|