[LTP] [PATCH v2] cachestat01.c: Add cachestat() testcheck

Richard Palethorpe rpalethorpe@suse.de
Mon Nov 20 12:25:54 CET 2023


Hello,

Wei Gao via ltp <ltp@lists.linux.it> writes:

> Signed-off-by: Wei Gao <wegao@suse.com>
> ---
>  configure.ac                                  |   1 +
>  include/lapi/cachestat.h                      |  36 +++
>  include/lapi/syscalls/aarch64.in              |   1 +
>  include/lapi/syscalls/arc.in                  |   1 +
>  include/lapi/syscalls/arm.in                  |   1 +
>  include/lapi/syscalls/hppa.in                 |   1 +
>  include/lapi/syscalls/i386.in                 |   1 +
>  include/lapi/syscalls/ia64.in                 |   1 +
>  include/lapi/syscalls/powerpc.in              |   1 +
>  include/lapi/syscalls/powerpc64.in            |   1 +
>  include/lapi/syscalls/s390.in                 |   1 +
>  include/lapi/syscalls/s390x.in                |   1 +
>  include/lapi/syscalls/sh.in                   |   1 +
>  include/lapi/syscalls/sparc.in                |   1 +
>  include/lapi/syscalls/sparc64.in              |   1 +
>  include/lapi/syscalls/x86_64.in               |   1 +
>  runtest/syscalls                              |   2 +
>  .../kernel/syscalls/cachestat/.gitignore      |   1 +
>  testcases/kernel/syscalls/cachestat/Makefile  |   8 +
>  .../kernel/syscalls/cachestat/cachestat01.c   | 260 ++++++++++++++++++
>  20 files changed, 322 insertions(+)
>  create mode 100644 include/lapi/cachestat.h
>  create mode 100644 testcases/kernel/syscalls/cachestat/.gitignore
>  create mode 100644 testcases/kernel/syscalls/cachestat/Makefile
>  create mode 100644 testcases/kernel/syscalls/cachestat/cachestat01.c
>
> diff --git a/configure.ac b/configure.ac
> index 662c4c058..4b5547b5b 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -93,6 +93,7 @@ AC_CHECK_FUNCS_ONCE([ \
>      epoll_pwait2 \
>      execveat \
>      faccessat2 \
> +    cachestat \
>      fallocate \
>      fchownat \
>      fsconfig \
> diff --git a/include/lapi/cachestat.h b/include/lapi/cachestat.h
> new file mode 100644
> index 000000000..fea390849
> --- /dev/null
> +++ b/include/lapi/cachestat.h
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) Linux Test Project, 2003-2023
> + * Author: Wei Gao <wegao@suse.com>
> + */
> +
> +#ifndef CACHESTAT_H
> +#define CACHESTAT_H
> +
> +#include "tst_test.h"
> +#include "config.h"
> +#include "lapi/syscalls.h"
> +
> +#ifndef HAVE_CACHESTAT
> +struct cachestat_range {
> +	__u64 off;
> +	__u64 len;
> +};
> +
> +struct cachestat {
> +	__u64 nr_cache;
> +	__u64 nr_dirty;
> +	__u64 nr_writeback;
> +	__u64 nr_evicted;
> +	__u64 nr_recently_evicted;
> +};
> +
> +int cachestat(unsigned int fd,
> +                struct cachestat_range *cstat_range,
> +                struct cachestat *cstat, unsigned int flags)
> +{
> +	return tst_syscall(__NR_cachestat, fd, cstat_range, cstat, flags);
> +}
> +#endif
> +
> +#endif /* CACHESTAT_H */
> diff --git a/include/lapi/syscalls/aarch64.in b/include/lapi/syscalls/aarch64.in
> index 2cb6c2d87..1c0218eae 100644
> --- a/include/lapi/syscalls/aarch64.in
> +++ b/include/lapi/syscalls/aarch64.in
> @@ -297,4 +297,5 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
>  _sysctl 1078
> diff --git a/include/lapi/syscalls/arc.in b/include/lapi/syscalls/arc.in
> index 3e2ee9061..5d7cd6ca4 100644
> --- a/include/lapi/syscalls/arc.in
> +++ b/include/lapi/syscalls/arc.in
> @@ -317,3 +317,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/arm.in b/include/lapi/syscalls/arm.in
> index 7bdbca533..e41a7e576 100644
> --- a/include/lapi/syscalls/arm.in
> +++ b/include/lapi/syscalls/arm.in
> @@ -395,3 +395,4 @@ faccessat2 (__NR_SYSCALL_BASE+439)
>  epoll_pwait2 (__NR_SYSCALL_BASE+441)
>  quotactl_fd (__NR_SYSCALL_BASE+443)
>  futex_waitv (__NR_SYSCALL_BASE+449)
> +cachestat (__NR_SYSCALL_BASE+451)
> diff --git a/include/lapi/syscalls/hppa.in b/include/lapi/syscalls/hppa.in
> index 8ebdafafb..2772e7334 100644
> --- a/include/lapi/syscalls/hppa.in
> +++ b/include/lapi/syscalls/hppa.in
> @@ -44,3 +44,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/i386.in b/include/lapi/syscalls/i386.in
> index 1472631c4..2d341182e 100644
> --- a/include/lapi/syscalls/i386.in
> +++ b/include/lapi/syscalls/i386.in
> @@ -431,3 +431,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/ia64.in b/include/lapi/syscalls/ia64.in
> index 0ea6e9722..141c6be51 100644
> --- a/include/lapi/syscalls/ia64.in
> +++ b/include/lapi/syscalls/ia64.in
> @@ -344,3 +344,4 @@ faccessat2 1463
>  epoll_pwait2 1465
>  quotactl_fd 1467
>  futex_waitv 1473
> +cachestat 1475
> diff --git a/include/lapi/syscalls/powerpc.in b/include/lapi/syscalls/powerpc.in
> index 545d9d3d6..67e928951 100644
> --- a/include/lapi/syscalls/powerpc.in
> +++ b/include/lapi/syscalls/powerpc.in
> @@ -424,3 +424,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/powerpc64.in b/include/lapi/syscalls/powerpc64.in
> index 545d9d3d6..67e928951 100644
> --- a/include/lapi/syscalls/powerpc64.in
> +++ b/include/lapi/syscalls/powerpc64.in
> @@ -424,3 +424,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/s390.in b/include/lapi/syscalls/s390.in
> index 7213ac5f8..b456ea408 100644
> --- a/include/lapi/syscalls/s390.in
> +++ b/include/lapi/syscalls/s390.in
> @@ -411,3 +411,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/s390x.in b/include/lapi/syscalls/s390x.in
> index 879012e2b..2c57eacdf 100644
> --- a/include/lapi/syscalls/s390x.in
> +++ b/include/lapi/syscalls/s390x.in
> @@ -359,3 +359,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/sh.in b/include/lapi/syscalls/sh.in
> index 7d5192a27..25eb9bb26 100644
> --- a/include/lapi/syscalls/sh.in
> +++ b/include/lapi/syscalls/sh.in
> @@ -405,3 +405,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/sparc.in b/include/lapi/syscalls/sparc.in
> index 91d2fb1c2..e934591dd 100644
> --- a/include/lapi/syscalls/sparc.in
> +++ b/include/lapi/syscalls/sparc.in
> @@ -410,3 +410,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/sparc64.in b/include/lapi/syscalls/sparc64.in
> index 1f2fc59b7..4c489e38d 100644
> --- a/include/lapi/syscalls/sparc64.in
> +++ b/include/lapi/syscalls/sparc64.in
> @@ -375,3 +375,4 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
> diff --git a/include/lapi/syscalls/x86_64.in b/include/lapi/syscalls/x86_64.in
> index dc61aa56e..4afea6019 100644
> --- a/include/lapi/syscalls/x86_64.in
> +++ b/include/lapi/syscalls/x86_64.in
> @@ -352,6 +352,7 @@ faccessat2 439
>  epoll_pwait2 441
>  quotactl_fd 443
>  futex_waitv 449
> +cachestat 451
>  rt_sigaction 512
>  rt_sigreturn 513
>  ioctl 514
> diff --git a/runtest/syscalls b/runtest/syscalls
> index 4fb76584f..b84b2d2ce 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -59,6 +59,8 @@ capset04 capset04
>  
>  cacheflush01 cacheflush01
>  
> +cachestat01 cachestat01
> +
>  chdir01 chdir01
>  chdir01A symlink01 -T chdir01
>  chdir04 chdir04
> diff --git a/testcases/kernel/syscalls/cachestat/.gitignore b/testcases/kernel/syscalls/cachestat/.gitignore
> new file mode 100644
> index 000000000..870bceae4
> --- /dev/null
> +++ b/testcases/kernel/syscalls/cachestat/.gitignore
> @@ -0,0 +1 @@
> +/cachestat01
> diff --git a/testcases/kernel/syscalls/cachestat/Makefile b/testcases/kernel/syscalls/cachestat/Makefile
> new file mode 100644
> index 000000000..49238eee0
> --- /dev/null
> +++ b/testcases/kernel/syscalls/cachestat/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +# Copyright (c) 2023 Wei Gao <wegao@suse.com>
> +
> +top_srcdir		?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/cachestat/cachestat01.c b/testcases/kernel/syscalls/cachestat/cachestat01.c
> new file mode 100644
> index 000000000..9ad432b59
> --- /dev/null
> +++ b/testcases/kernel/syscalls/cachestat/cachestat01.c
> @@ -0,0 +1,260 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2023 Andre Przywara <andre.przywara@arm.com>
> + * Copyright (c) 2023 Wei Gao <wegao@suse.com>
> + *
> + */
> +
> +/*\
> + * [Description]
> + *
> + * Verify that cachestat() executes successfully
> + *
> + */
> +
> +#include <stdio.h>
> +#include <math.h>
> +#include <unistd.h> /* _SC_PAGESIZE */
> +#include <stdbool.h>
> +#include <stdlib.h>
> +#include "tst_test.h"
> +#include "lapi/syscalls.h"
> +#include "lapi/cachestat.h"
> +
> +#define SAFE_FREE(p) { if (p) { free(p); (p) = NULL; } }
> +#define TMPFS_MAGIC 0x01021994

This is already defined as TST_TMPFS_MAGIC

> +#define TEST_NORMAL_FILE "tmpfilecachestat"
> +#define SHM_FILE "tmpshmcstat"
> +#define NUM_PAGES 4
> +
> +static int fd;
> +static int fd_shm;
> +static size_t PS;
> +static size_t filesize;
> +static char *data;
> +static int random_fd;
> +
> +/*
> + * 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))

fstatfs01 expects fstatfs to succeed on all file systems. So we should
here too.

> +		return false;
> +
> +	return statfs_buf.f_type == TMPFS_MAGIC;
> +}
> +
> +static void print_cachestat(struct cachestat *cs)
> +{
> +	tst_res(TINFO,
> +			"Using cachestat: Cached: %llu, Dirty: %llu, Writeback: %llu, Evicted: %llu, Recently Evicted: %llu",
> +			cs->nr_cache, cs->nr_dirty, cs->nr_writeback,
> +			cs->nr_evicted, cs->nr_recently_evicted);
> +}
> +
> +static const char * const dev_files[] = {
> +	"/dev/zero", "/dev/null", "/dev/urandom",
> +	"/proc/version", "/proc"
> +};
> +
> +static bool write_exactly(int write_fd)
> +{
> +	char *cursor;
> +	int remained;
> +
> +	remained = filesize;
> +	cursor = data;
> +	while (remained) {
> +		ssize_t read_len = SAFE_READ(1, random_fd, cursor,
> remained);

If len_strict is set then there is no point having a loop.

> +
> +		if (read_len <= 0)
> +			tst_brk(TBROK | TERRNO, "Unable to read from urandom.");
> +

read_len can't be negative.

> +		remained -= read_len;
> +		cursor += read_len;
> +	}
> +
> +	remained = filesize;
> +	cursor = data;
> +	while (remained) {
> +		ssize_t write_len = SAFE_WRITE(1, write_fd, cursor, remained);
> +
> +		if (write_len <= 0)
> +			tst_brk(TBROK | TERRNO, "Unable write random
> data to file.");

Same issues again.

Probably 4 pages is too many to rely on being read atomically. I don't
know about FS like btrfs and xfs, but only a single page is used for
buffering sequence files (IIRC).

Why not just use tst_fill_file?

Generally I don't like using random data anyway. If it has any effect at
all, it could make bug reproduction more difficult.

> +
> +		remained -= write_len;
> +		cursor += write_len;
> +	}
> +
> +	return true;
> +}
> +
> +/*
> + * 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.
> + */
> +static void test_cachestat(const char *filename, bool write_random, bool create,
> +		bool test_fsync, int open_flags, mode_t open_mode)
> +{
> +	struct cachestat cs;
> +	struct cachestat_range cs_range = { 0, filesize };
> +
> +	if (!fcntl(fd, F_GETFD))
> +		SAFE_CLOSE(fd);
> +	fd = SAFE_OPEN(filename, open_flags, open_mode);
> +
> +	if (write_random) {
> +		if (!write_exactly(fd))
> +			tst_brk(TBROK | TERRNO, "Unable to access
> urandom.");

How can write_exactly return false?

> +	}
> +
> +	TST_EXP_PASS(cachestat(fd, &cs_range, &cs, 0));
> +
> +	print_cachestat(&cs);
> +
> +	if (write_random) {
> +		if (cs.nr_cache + cs.nr_evicted != NUM_PAGES) {
> +			tst_brk(TBROK | TERRNO,
> +					"Total number of cached and
> evicted pages is off.");

I suppose this was in the selftest, so we can trust that these counters
are updated synchronously.

We probably should print the number of cached and evicted pages though.

> +		}
> +	}
> +
> +	if (test_fsync) {
> +		if (is_on_tmpfs(fd)) {
> +			tst_res(TCONF, "skip fsync check on tmpfs");
> +		} else if (fsync(fd)) {
> +			tst_brk(TBROK | TERRNO, "fsync fails.");
> +		} else {
> +			TST_EXP_PASS(cachestat(fd, &cs_range, &cs, 0));
> +			print_cachestat(&cs);
> +			if (cs.nr_dirty) {
> +				tst_brk(TBROK | TERRNO,
> +						"Number of dirty should be zero after fsync.");
> +			} else {
> +				tst_res(TPASS, "Cachestat (after fsync) pass.");
> +			}
> +		}
> +	}
> +
> +	close(fd);
> +
> +	if (create)
> +		remove(filename);
> +
> +	tst_res(TPASS, "cachestat works with file %s", filename);
> +}
> +
> +static void test_incorrect_file(void)
> +{
> +	TST_EXP_FAIL(cachestat(-1, NULL, NULL, 0), EBADF);
> +}
> +
> +static void test_virtual_file(void)
> +{
> +	for (unsigned long i = 0; i < sizeof(dev_files) / sizeof(*dev_files); i++) {
> +		const char *dev_filename = dev_files[i];
> +
> +		test_cachestat(dev_filename, false, false, false, O_RDONLY, 0400);
> +	}
> +}
> +
> +static void test_normal_file(void)
> +{
> +	test_cachestat(TEST_NORMAL_FILE, true, true, false, O_CREAT | O_RDWR, 0600);
> +	test_cachestat(TEST_NORMAL_FILE, true, true, true, O_CREAT | O_RDWR, 0600);
> +}
> +
> +static void check_cachestat_range(struct cachestat_range *ptr_cs_range, __u64 expect_num_pages)
> +{
> +	struct cachestat cs;
> +
> +	TST_EXP_PASS(cachestat(fd_shm, ptr_cs_range, &cs, 0));
> +	print_cachestat(&cs);
> +	if (cs.nr_cache + cs.nr_evicted != expect_num_pages) {
> +		tst_brk(TFAIL | TERRNO,
> +				"Total number of cached and evicted pages is off.");
> +	} else {
> +		tst_res(TPASS,
> +				"Cachestat range check pass.");
> +	}
> +}
> +
> +static void test_share_memory(void)
> +{
> +
> +	struct cachestat_range cs_range = { 0, filesize};
> +
> +	if (ftruncate(fd_shm, filesize))
> +		tst_brk(TFAIL | TERRNO, "Unable to truncate shmem file.");
> +
> +	if (!write_exactly(fd_shm))
> +		tst_brk(TFAIL | TERRNO, "Unable to write to shmem file.");
> +
> +	check_cachestat_range(&cs_range, NUM_PAGES);
> +
> +	size_t compute_len = PS * NUM_PAGES / 2;
> +	unsigned long num_pages = ceil((double)NUM_PAGES / 2);
> +
> +	cs_range.off = PS;
> +	cs_range.len = compute_len;
> +	check_cachestat_range(&cs_range, num_pages);
> +}
> +
> +static struct tcase {
> +	void (*tfunc)(void);
> +} tcases[] = {
> +	{&test_incorrect_file},
> +	{&test_virtual_file},
> +	{&test_normal_file},
> +	{&test_share_memory}
> +};
> +
> +static void run(unsigned int i)
> +{
> +	tcases[i].tfunc();
> +}
> +
> +
> +static void setup(void)
> +{
> +
> +	PS = sysconf(_SC_PAGESIZE);
> +	filesize = NUM_PAGES * PS;
> +	data = tst_alloc(filesize);

This can be allocated using test.bufs which will also handle cleanup.

> +	random_fd = SAFE_OPEN("/dev/urandom", O_RDONLY);
> +
> +	/* setup for test_share_memory case */
> +	fd_shm = shm_open(SHM_FILE, O_CREAT | O_RDWR, 0700);
> +	if (fd_shm < 0)
> +		tst_brk(TFAIL | TERRNO, "Unable to create shmem file.");
> +}
> +
> +static void cleanup(void)
> +{
> +	SAFE_CLOSE(random_fd);
> +
> +	/* cleanup for test_normal_file case*/
> +	if (!fcntl(fd, F_GETFD))
> +		SAFE_CLOSE(fd);
> +	remove(TEST_NORMAL_FILE);
> +
> +	/* cleanup for test_share_memory case */
> +	SAFE_CLOSE(fd_shm);
> +	shm_unlink(SHM_FILE);
> +}
> +
> +static struct tst_test test = {
> +	.test = run,
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.needs_tmpdir = 1,
> +	.min_kver = "6.5.0"

This would benefit from being run on all filesystems!

> +};
> -- 
> 2.35.3


-- 
Thank you,
Richard.


More information about the ltp mailing list