brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 632ab44 Raw
320 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#define __SANE_USERSPACE_TYPES__ // Use ll644 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	922 23static const char * const dev_files[] = {24	"/dev/zero", "/dev/null", "/dev/urandom",25	"/proc/version", "/proc"26};27 28void 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 36bool 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;89out_free_data:90	free(data);91close_random_fd:92	close(random_fd);93out: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 */101static 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 it113 * (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 dirty116 * pages.117 */118static 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 196out1:197	close(fd);198 199	if (create)200		remove(filename);201out:202	return ret;203}204 205bool 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 251close_fd:252	shm_unlink(filename);253out:254	return ret;255}256 257int 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}320