diff options
Diffstat (limited to 'tools/testing/selftests/mm/vm_util.c')
| -rw-r--r-- | tools/testing/selftests/mm/vm_util.c | 151 | 
1 files changed, 151 insertions, 0 deletions
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c new file mode 100644 index 000000000000..40e795624ff3 --- /dev/null +++ b/tools/testing/selftests/mm/vm_util.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <string.h> +#include <fcntl.h> +#include "../kselftest.h" +#include "vm_util.h" + +#define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" +#define SMAP_FILE_PATH "/proc/self/smaps" +#define MAX_LINE_LENGTH 500 + +uint64_t pagemap_get_entry(int fd, char *start) +{ +	const unsigned long pfn = (unsigned long)start / getpagesize(); +	uint64_t entry; +	int ret; + +	ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry)); +	if (ret != sizeof(entry)) +		ksft_exit_fail_msg("reading pagemap failed\n"); +	return entry; +} + +bool pagemap_is_softdirty(int fd, char *start) +{ +	uint64_t entry = pagemap_get_entry(fd, start); + +	// Check if dirty bit (55th bit) is set +	return entry & 0x0080000000000000ull; +} + +bool pagemap_is_swapped(int fd, char *start) +{ +	uint64_t entry = pagemap_get_entry(fd, start); + +	return entry & 0x4000000000000000ull; +} + +bool pagemap_is_populated(int fd, char *start) +{ +	uint64_t entry = pagemap_get_entry(fd, start); + +	/* Present or swapped. */ +	return entry & 0xc000000000000000ull; +} + +unsigned long pagemap_get_pfn(int fd, char *start) +{ +	uint64_t entry = pagemap_get_entry(fd, start); + +	/* If present (63th bit), PFN is at bit 0 -- 54. */ +	if (entry & 0x8000000000000000ull) +		return entry & 0x007fffffffffffffull; +	return -1ul; +} + +void clear_softdirty(void) +{ +	int ret; +	const char *ctrl = "4"; +	int fd = open("/proc/self/clear_refs", O_WRONLY); + +	if (fd < 0) +		ksft_exit_fail_msg("opening clear_refs failed\n"); +	ret = write(fd, ctrl, strlen(ctrl)); +	close(fd); +	if (ret != strlen(ctrl)) +		ksft_exit_fail_msg("writing clear_refs failed\n"); +} + +bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len) +{ +	while (fgets(buf, len, fp)) { +		if (!strncmp(buf, pattern, strlen(pattern))) +			return true; +	} +	return false; +} + +uint64_t read_pmd_pagesize(void) +{ +	int fd; +	char buf[20]; +	ssize_t num_read; + +	fd = open(PMD_SIZE_FILE_PATH, O_RDONLY); +	if (fd == -1) +		ksft_exit_fail_msg("Open hpage_pmd_size failed\n"); + +	num_read = read(fd, buf, 19); +	if (num_read < 1) { +		close(fd); +		ksft_exit_fail_msg("Read hpage_pmd_size failed\n"); +	} +	buf[num_read] = '\0'; +	close(fd); + +	return strtoul(buf, NULL, 10); +} + +bool __check_huge(void *addr, char *pattern, int nr_hpages, +		  uint64_t hpage_size) +{ +	uint64_t thp = -1; +	int ret; +	FILE *fp; +	char buffer[MAX_LINE_LENGTH]; +	char addr_pattern[MAX_LINE_LENGTH]; + +	ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-", +		       (unsigned long) addr); +	if (ret >= MAX_LINE_LENGTH) +		ksft_exit_fail_msg("%s: Pattern is too long\n", __func__); + +	fp = fopen(SMAP_FILE_PATH, "r"); +	if (!fp) +		ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH); + +	if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer))) +		goto err_out; + +	/* +	 * Fetch the pattern in the same block and check the number of +	 * hugepages. +	 */ +	if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer))) +		goto err_out; + +	snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern); + +	if (sscanf(buffer, addr_pattern, &thp) != 1) +		ksft_exit_fail_msg("Reading smap error\n"); + +err_out: +	fclose(fp); +	return thp == (nr_hpages * (hpage_size >> 10)); +} + +bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size) +{ +	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size); +} + +bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size) +{ +	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size); +} + +bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size) +{ +	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size); +}  |