diff options
Diffstat (limited to 'tools/testing/selftests/mm')
19 files changed, 494 insertions, 215 deletions
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index eb5f39a2668b..60b8feb6a5ec 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -12,7 +12,7 @@ uname_M := $(shell uname -m 2>/dev/null || echo not)  else  uname_M := $(shell echo $(CROSS_COMPILE) | grep -o '^[a-z0-9]\+')  endif -ARCH ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/' -e 's/ppc64.*/ppc64/') +ARCH ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/' -e 's/ppc64.*/powerpc/')  endif  # Without this, failed build products remain, with up-to-date timestamps, @@ -32,7 +32,7 @@ endif  # LDLIBS.  MAKEFLAGS += --no-builtin-rules -CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) +CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)  LDLIBS = -lrt -lpthread -lm  TEST_GEN_FILES = cow @@ -98,13 +98,13 @@ TEST_GEN_FILES += $(BINARIES_64)  endif  else -ifneq (,$(findstring $(ARCH),ppc64)) +ifneq (,$(findstring $(ARCH),powerpc))  TEST_GEN_FILES += protection_keys  endif  endif -ifneq (,$(filter $(ARCH),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sparc64 x86_64)) +ifneq (,$(filter $(ARCH),arm64 ia64 mips64 parisc64 powerpc riscv64 s390x sparc64 x86_64))  TEST_GEN_FILES += va_high_addr_switch  TEST_GEN_FILES += virtual_address_range  TEST_GEN_FILES += write_to_hugetlbfs diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c index 533999b6c284..4f42eb7d7636 100644 --- a/tools/testing/selftests/mm/compaction_test.c +++ b/tools/testing/selftests/mm/compaction_test.c @@ -177,7 +177,7 @@ int main(int argc, char **argv)  	ksft_print_header();  	if (prereq() || geteuid()) -		return ksft_exit_skip("Prerequisites unsatisfied\n"); +		ksft_exit_skip("Prerequisites unsatisfied\n");  	ksft_set_plan(1); @@ -225,7 +225,7 @@ int main(int argc, char **argv)  	}  	if (check_compaction(mem_free, hugepage_size) == 0) -		return ksft_exit_pass(); +		ksft_exit_pass(); -	return ksft_exit_fail(); +	ksft_exit_fail();  } diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index 363bf5f801be..32c6ccc2a6be 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -199,7 +199,7 @@ static int child_vmsplice_memcmp_fn(char *mem, size_t size,  typedef int (*child_fn)(char *mem, size_t size, struct comm_pipes *comm_pipes);  static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect, -				  child_fn fn) +		child_fn fn, bool xfail)  {  	struct comm_pipes comm_pipes;  	char buf; @@ -247,33 +247,47 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect,  	else  		ret = -EINVAL; -	ksft_test_result(!ret, "No leak from parent into child\n"); +	if (!ret) { +		ksft_test_result_pass("No leak from parent into child\n"); +	} else if (xfail) { +		/* +		 * With hugetlb, some vmsplice() tests are currently expected to +		 * fail because (a) harder to fix and (b) nobody really cares. +		 * Flag them as expected failure for now. +		 */ +		ksft_test_result_xfail("Leak from parent into child\n"); +	} else { +		ksft_test_result_fail("Leak from parent into child\n"); +	}  close_comm_pipes:  	close_comm_pipes(&comm_pipes);  } -static void test_cow_in_parent(char *mem, size_t size) +static void test_cow_in_parent(char *mem, size_t size, bool is_hugetlb)  { -	do_test_cow_in_parent(mem, size, false, child_memcmp_fn); +	do_test_cow_in_parent(mem, size, false, child_memcmp_fn, false);  } -static void test_cow_in_parent_mprotect(char *mem, size_t size) +static void test_cow_in_parent_mprotect(char *mem, size_t size, bool is_hugetlb)  { -	do_test_cow_in_parent(mem, size, true, child_memcmp_fn); +	do_test_cow_in_parent(mem, size, true, child_memcmp_fn, false);  } -static void test_vmsplice_in_child(char *mem, size_t size) +static void test_vmsplice_in_child(char *mem, size_t size, bool is_hugetlb)  { -	do_test_cow_in_parent(mem, size, false, child_vmsplice_memcmp_fn); +	do_test_cow_in_parent(mem, size, false, child_vmsplice_memcmp_fn, +			      is_hugetlb);  } -static void test_vmsplice_in_child_mprotect(char *mem, size_t size) +static void test_vmsplice_in_child_mprotect(char *mem, size_t size, +		bool is_hugetlb)  { -	do_test_cow_in_parent(mem, size, true, child_vmsplice_memcmp_fn); +	do_test_cow_in_parent(mem, size, true, child_vmsplice_memcmp_fn, +			      is_hugetlb);  }  static void do_test_vmsplice_in_parent(char *mem, size_t size, -				       bool before_fork) +				       bool before_fork, bool xfail)  {  	struct iovec iov = {  		.iov_base = mem, @@ -355,8 +369,18 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size,  		}  	} -	ksft_test_result(!memcmp(old, new, transferred), -			 "No leak from child into parent\n"); +	if (!memcmp(old, new, transferred)) { +		ksft_test_result_pass("No leak from child into parent\n"); +	} else if (xfail) { +		/* +		 * With hugetlb, some vmsplice() tests are currently expected to +		 * fail because (a) harder to fix and (b) nobody really cares. +		 * Flag them as expected failure for now. +		 */ +		ksft_test_result_xfail("Leak from child into parent\n"); +	} else { +		ksft_test_result_fail("Leak from child into parent\n"); +	}  close_pipe:  	close(fds[0]);  	close(fds[1]); @@ -367,14 +391,14 @@ free:  	free(new);  } -static void test_vmsplice_before_fork(char *mem, size_t size) +static void test_vmsplice_before_fork(char *mem, size_t size, bool is_hugetlb)  { -	do_test_vmsplice_in_parent(mem, size, true); +	do_test_vmsplice_in_parent(mem, size, true, is_hugetlb);  } -static void test_vmsplice_after_fork(char *mem, size_t size) +static void test_vmsplice_after_fork(char *mem, size_t size, bool is_hugetlb)  { -	do_test_vmsplice_in_parent(mem, size, false); +	do_test_vmsplice_in_parent(mem, size, false, is_hugetlb);  }  #ifdef LOCAL_CONFIG_HAVE_LIBURING @@ -529,12 +553,12 @@ close_comm_pipes:  	close_comm_pipes(&comm_pipes);  } -static void test_iouring_ro(char *mem, size_t size) +static void test_iouring_ro(char *mem, size_t size, bool is_hugetlb)  {  	do_test_iouring(mem, size, false);  } -static void test_iouring_fork(char *mem, size_t size) +static void test_iouring_fork(char *mem, size_t size, bool is_hugetlb)  {  	do_test_iouring(mem, size, true);  } @@ -678,37 +702,41 @@ free_tmp:  	free(tmp);  } -static void test_ro_pin_on_shared(char *mem, size_t size) +static void test_ro_pin_on_shared(char *mem, size_t size, bool is_hugetlb)  {  	do_test_ro_pin(mem, size, RO_PIN_TEST_SHARED, false);  } -static void test_ro_fast_pin_on_shared(char *mem, size_t size) +static void test_ro_fast_pin_on_shared(char *mem, size_t size, bool is_hugetlb)  {  	do_test_ro_pin(mem, size, RO_PIN_TEST_SHARED, true);  } -static void test_ro_pin_on_ro_previously_shared(char *mem, size_t size) +static void test_ro_pin_on_ro_previously_shared(char *mem, size_t size, +		bool is_hugetlb)  {  	do_test_ro_pin(mem, size, RO_PIN_TEST_PREVIOUSLY_SHARED, false);  } -static void test_ro_fast_pin_on_ro_previously_shared(char *mem, size_t size) +static void test_ro_fast_pin_on_ro_previously_shared(char *mem, size_t size, +		bool is_hugetlb)  {  	do_test_ro_pin(mem, size, RO_PIN_TEST_PREVIOUSLY_SHARED, true);  } -static void test_ro_pin_on_ro_exclusive(char *mem, size_t size) +static void test_ro_pin_on_ro_exclusive(char *mem, size_t size, +		bool is_hugetlb)  {  	do_test_ro_pin(mem, size, RO_PIN_TEST_RO_EXCLUSIVE, false);  } -static void test_ro_fast_pin_on_ro_exclusive(char *mem, size_t size) +static void test_ro_fast_pin_on_ro_exclusive(char *mem, size_t size, +		bool is_hugetlb)  {  	do_test_ro_pin(mem, size, RO_PIN_TEST_RO_EXCLUSIVE, true);  } -typedef void (*test_fn)(char *mem, size_t size); +typedef void (*test_fn)(char *mem, size_t size, bool hugetlb);  static void do_run_with_base_page(test_fn fn, bool swapout)  { @@ -740,7 +768,7 @@ static void do_run_with_base_page(test_fn fn, bool swapout)  		}  	} -	fn(mem, pagesize); +	fn(mem, pagesize, false);  munmap:  	munmap(mem, pagesize);  } @@ -904,7 +932,7 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run, size_t thpsize)  		break;  	} -	fn(mem, size); +	fn(mem, size, false);  munmap:  	munmap(mmap_mem, mmap_size);  	if (mremap_mem != MAP_FAILED) @@ -997,7 +1025,7 @@ static void run_with_hugetlb(test_fn fn, const char *desc, size_t hugetlbsize)  	}  	munmap(dummy, hugetlbsize); -	fn(mem, hugetlbsize); +	fn(mem, hugetlbsize, true);  munmap:  	munmap(mem, hugetlbsize);  } @@ -1036,7 +1064,7 @@ static const struct test_case anon_test_cases[] = {  	 */  	{  		"vmsplice() + unmap in child", -		test_vmsplice_in_child +		test_vmsplice_in_child,  	},  	/*  	 * vmsplice() test, but do an additional mprotect(PROT_READ)+ @@ -1044,7 +1072,7 @@ static const struct test_case anon_test_cases[] = {  	 */  	{  		"vmsplice() + unmap in child with mprotect() optimization", -		test_vmsplice_in_child_mprotect +		test_vmsplice_in_child_mprotect,  	},  	/*  	 * vmsplice() [R/O GUP] in parent before fork(), unmap in parent after @@ -1322,23 +1350,31 @@ close_comm_pipes:  	close_comm_pipes(&comm_pipes);  } -static void test_anon_thp_collapse_unshared(char *mem, size_t size) +static void test_anon_thp_collapse_unshared(char *mem, size_t size, +		bool is_hugetlb)  { +	assert(!is_hugetlb);  	do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_UNSHARED);  } -static void test_anon_thp_collapse_fully_shared(char *mem, size_t size) +static void test_anon_thp_collapse_fully_shared(char *mem, size_t size, +		bool is_hugetlb)  { +	assert(!is_hugetlb);  	do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_FULLY_SHARED);  } -static void test_anon_thp_collapse_lower_shared(char *mem, size_t size) +static void test_anon_thp_collapse_lower_shared(char *mem, size_t size, +		bool is_hugetlb)  { +	assert(!is_hugetlb);  	do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_LOWER_SHARED);  } -static void test_anon_thp_collapse_upper_shared(char *mem, size_t size) +static void test_anon_thp_collapse_upper_shared(char *mem, size_t size, +		bool is_hugetlb)  { +	assert(!is_hugetlb);  	do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_UPPER_SHARED);  } @@ -1779,5 +1815,5 @@ int main(int argc, char **argv)  	if (err)  		ksft_exit_fail_msg("%d out of %d tests failed\n",  				   err, ksft_test_num()); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c index ad168d35b23b..9423ad439a61 100644 --- a/tools/testing/selftests/mm/gup_longterm.c +++ b/tools/testing/selftests/mm/gup_longterm.c @@ -118,15 +118,22 @@ static void do_test(int fd, size_t size, enum test_type type, bool shared)  		return;  	} -	/* -	 * Fault in the page writable such that GUP-fast can eventually pin -	 * it immediately. -	 */ +	/* Fault in the page such that GUP-fast can pin it directly. */  	memset(mem, 0, size);  	switch (type) {  	case TEST_TYPE_RO:  	case TEST_TYPE_RO_FAST: +		/* +		 * Cover more cases regarding unsharing decisions when +		 * long-term R/O pinning by mapping the page R/O. +		 */ +		ret = mprotect(mem, size, PROT_READ); +		if (ret) { +			ksft_test_result_fail("mprotect() failed\n"); +			goto munmap; +		} +		/* FALLTHROUGH */  	case TEST_TYPE_RW:  	case TEST_TYPE_RW_FAST: {  		struct pin_longterm_test args; @@ -228,6 +235,7 @@ static void do_test(int fd, size_t size, enum test_type type, bool shared)  		assert(false);  	} +munmap:  	munmap(mem, size);  } @@ -456,5 +464,5 @@ int main(int argc, char **argv)  	if (err)  		ksft_exit_fail_msg("%d out of %d tests failed\n",  				   err, ksft_test_num()); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c index 18a49c70d4c6..bd335cf9bc0e 100644 --- a/tools/testing/selftests/mm/gup_test.c +++ b/tools/testing/selftests/mm/gup_test.c @@ -228,7 +228,7 @@ int main(int argc, char **argv)  			break;  		}  		ksft_test_result_skip("Please run this test as root\n"); -		return ksft_exit_pass(); +		ksft_exit_pass();  	}  	p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0); @@ -267,5 +267,5 @@ int main(int argc, char **argv)  	free(tid); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c index d01e8d4901d0..8f122a0f0828 100644 --- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c +++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c @@ -27,9 +27,9 @@  #include "vm_util.h"  #include "../kselftest.h" -#define MMAP_SIZE (1 << 21)  #define INLOOP_ITER 100 +size_t mmap_size;  char *huge_ptr;  /* Touch the memory while it is being madvised() */ @@ -44,7 +44,7 @@ void *touch(void *unused)  void *madv(void *unused)  {  	for (int i = 0; i < INLOOP_ITER; i++) -		madvise(huge_ptr, MMAP_SIZE, MADV_DONTNEED); +		madvise(huge_ptr, mmap_size, MADV_DONTNEED);  	return NULL;  } @@ -59,7 +59,7 @@ void *map_extra(void *unused)  	void *ptr;  	for (int i = 0; i < INLOOP_ITER; i++) { -		ptr = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, +		ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,  			   MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,  			   -1, 0); @@ -93,14 +93,16 @@ int main(void)  			       free_hugepages);  	} +	mmap_size = default_huge_page_size(); +  	while (max--) { -		huge_ptr = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, +		huge_ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,  				MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,  				-1, 0);  		if ((unsigned long)huge_ptr == -1) { -			ksft_exit_skip("Failed to allocated huge page\n"); -			return KSFT_SKIP; +			ksft_test_result_fail("Failed to allocate huge page\n"); +			return KSFT_FAIL;  		}  		pthread_create(&thread1, NULL, madv, NULL); @@ -117,7 +119,7 @@ int main(void)  		}  		/* Unmap and restart */ -		munmap(huge_ptr, MMAP_SIZE); +		munmap(huge_ptr, mmap_size);  	}  	return KSFT_PASS; diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c index d615767e396b..37de82da9be7 100644 --- a/tools/testing/selftests/mm/ksm_functional_tests.c +++ b/tools/testing/selftests/mm/ksm_functional_tests.c @@ -28,6 +28,15 @@  #define MiB (1024 * KiB)  #define FORK_EXEC_CHILD_PRG_NAME "ksm_fork_exec_child" +#define MAP_MERGE_FAIL ((void *)-1) +#define MAP_MERGE_SKIP ((void *)-2) + +enum ksm_merge_mode { +	KSM_MERGE_PRCTL, +	KSM_MERGE_MADVISE, +	KSM_MERGE_NONE, /* PRCTL already set */ +}; +  static int mem_fd;  static int ksm_fd;  static int ksm_full_scans_fd; @@ -146,33 +155,34 @@ static int ksm_unmerge(void)  	return 0;  } -static char *mmap_and_merge_range(char val, unsigned long size, int prot, -				  bool use_prctl) +static char *__mmap_and_merge_range(char val, unsigned long size, int prot, +				  enum ksm_merge_mode mode)  {  	char *map; +	char *err_map = MAP_MERGE_FAIL;  	int ret;  	/* Stabilize accounting by disabling KSM completely. */  	if (ksm_unmerge()) { -		ksft_test_result_fail("Disabling (unmerging) KSM failed\n"); -		return MAP_FAILED; +		ksft_print_msg("Disabling (unmerging) KSM failed\n"); +		return err_map;  	}  	if (get_my_merging_pages() > 0) { -		ksft_test_result_fail("Still pages merged\n"); -		return MAP_FAILED; +		ksft_print_msg("Still pages merged\n"); +		return err_map;  	}  	map = mmap(NULL, size, PROT_READ|PROT_WRITE,  		   MAP_PRIVATE|MAP_ANON, -1, 0);  	if (map == MAP_FAILED) { -		ksft_test_result_fail("mmap() failed\n"); -		return MAP_FAILED; +		ksft_print_msg("mmap() failed\n"); +		return err_map;  	}  	/* Don't use THP. Ignore if THP are not around on a kernel. */  	if (madvise(map, size, MADV_NOHUGEPAGE) && errno != EINVAL) { -		ksft_test_result_fail("MADV_NOHUGEPAGE failed\n"); +		ksft_print_msg("MADV_NOHUGEPAGE failed\n");  		goto unmap;  	} @@ -180,27 +190,36 @@ static char *mmap_and_merge_range(char val, unsigned long size, int prot,  	memset(map, val, size);  	if (mprotect(map, size, prot)) { -		ksft_test_result_skip("mprotect() failed\n"); +		ksft_print_msg("mprotect() failed\n"); +		err_map = MAP_MERGE_SKIP;  		goto unmap;  	} -	if (use_prctl) { +	switch (mode) { +	case KSM_MERGE_PRCTL:  		ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0);  		if (ret < 0 && errno == EINVAL) { -			ksft_test_result_skip("PR_SET_MEMORY_MERGE not supported\n"); +			ksft_print_msg("PR_SET_MEMORY_MERGE not supported\n"); +			err_map = MAP_MERGE_SKIP;  			goto unmap;  		} else if (ret) { -			ksft_test_result_fail("PR_SET_MEMORY_MERGE=1 failed\n"); +			ksft_print_msg("PR_SET_MEMORY_MERGE=1 failed\n");  			goto unmap;  		} -	} else if (madvise(map, size, MADV_MERGEABLE)) { -		ksft_test_result_fail("MADV_MERGEABLE failed\n"); -		goto unmap; +		break; +	case KSM_MERGE_MADVISE: +		if (madvise(map, size, MADV_MERGEABLE)) { +			ksft_print_msg("MADV_MERGEABLE failed\n"); +			goto unmap; +		} +		break; +	case KSM_MERGE_NONE: +		break;  	}  	/* Run KSM to trigger merging and wait. */  	if (ksm_merge()) { -		ksft_test_result_fail("Running KSM failed\n"); +		ksft_print_msg("Running KSM failed\n");  		goto unmap;  	} @@ -209,14 +228,31 @@ static char *mmap_and_merge_range(char val, unsigned long size, int prot,  	 * accounted differently (depending on kernel support).  	 */  	if (val && !get_my_merging_pages()) { -		ksft_test_result_fail("No pages got merged\n"); +		ksft_print_msg("No pages got merged\n");  		goto unmap;  	}  	return map;  unmap:  	munmap(map, size); -	return MAP_FAILED; +	return err_map; +} + +static char *mmap_and_merge_range(char val, unsigned long size, int prot, +				  enum ksm_merge_mode mode) +{ +	char *map; +	char *ret = MAP_FAILED; + +	map = __mmap_and_merge_range(val, size, prot, mode); +	if (map == MAP_MERGE_FAIL) +		ksft_test_result_fail("Merging memory failed"); +	else if (map == MAP_MERGE_SKIP) +		ksft_test_result_skip("Merging memory skipped"); +	else +		ret = map; + +	return ret;  }  static void test_unmerge(void) @@ -226,7 +262,7 @@ static void test_unmerge(void)  	ksft_print_msg("[RUN] %s\n", __func__); -	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, false); +	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, KSM_MERGE_MADVISE);  	if (map == MAP_FAILED)  		return; @@ -264,7 +300,7 @@ static void test_unmerge_zero_pages(void)  	}  	/* Let KSM deduplicate zero pages. */ -	map = mmap_and_merge_range(0x00, size, PROT_READ | PROT_WRITE, false); +	map = mmap_and_merge_range(0x00, size, PROT_READ | PROT_WRITE, KSM_MERGE_MADVISE);  	if (map == MAP_FAILED)  		return; @@ -312,7 +348,7 @@ static void test_unmerge_discarded(void)  	ksft_print_msg("[RUN] %s\n", __func__); -	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, false); +	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, KSM_MERGE_MADVISE);  	if (map == MAP_FAILED)  		return; @@ -344,7 +380,7 @@ static void test_unmerge_uffd_wp(void)  	ksft_print_msg("[RUN] %s\n", __func__); -	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, false); +	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, KSM_MERGE_MADVISE);  	if (map == MAP_FAILED)  		return; @@ -439,6 +475,36 @@ static void test_prctl(void)  	ksft_test_result_pass("Setting/clearing PR_SET_MEMORY_MERGE works\n");  } +static int test_child_ksm(void) +{ +	const unsigned int size = 2 * MiB; +	char *map; + +	/* Test if KSM is enabled for the process. */ +	if (prctl(PR_GET_MEMORY_MERGE, 0, 0, 0, 0) != 1) +		return -1; + +	/* Test if merge could really happen. */ +	map = __mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, KSM_MERGE_NONE); +	if (map == MAP_MERGE_FAIL) +		return -2; +	else if (map == MAP_MERGE_SKIP) +		return -3; + +	munmap(map, size); +	return 0; +} + +static void test_child_ksm_err(int status) +{ +	if (status == -1) +		ksft_test_result_fail("unexpected PR_GET_MEMORY_MERGE result in child\n"); +	else if (status == -2) +		ksft_test_result_fail("Merge in child failed\n"); +	else if (status == -3) +		ksft_test_result_skip("Merge in child skipped\n"); +} +  /* Verify that prctl ksm flag is inherited. */  static void test_prctl_fork(void)  { @@ -458,7 +524,7 @@ static void test_prctl_fork(void)  	child_pid = fork();  	if (!child_pid) { -		exit(prctl(PR_GET_MEMORY_MERGE, 0, 0, 0, 0)); +		exit(test_child_ksm());  	} else if (child_pid < 0) {  		ksft_test_result_fail("fork() failed\n");  		return; @@ -467,8 +533,11 @@ static void test_prctl_fork(void)  	if (waitpid(child_pid, &status, 0) < 0) {  		ksft_test_result_fail("waitpid() failed\n");  		return; -	} else if (WEXITSTATUS(status) != 1) { -		ksft_test_result_fail("unexpected PR_GET_MEMORY_MERGE result in child\n"); +	} + +	status = WEXITSTATUS(status); +	if (status) { +		test_child_ksm_err(status);  		return;  	} @@ -480,12 +549,6 @@ static void test_prctl_fork(void)  	ksft_test_result_pass("PR_SET_MEMORY_MERGE value is inherited\n");  } -static int ksm_fork_exec_child(void) -{ -	/* Test if KSM is enabled for the process. */ -	return prctl(PR_GET_MEMORY_MERGE, 0, 0, 0, 0) == 1; -} -  static void test_prctl_fork_exec(void)  {  	int ret, status; @@ -518,7 +581,7 @@ static void test_prctl_fork_exec(void)  		if (WIFEXITED(status)) {  			status = WEXITSTATUS(status);  			if (status) { -				ksft_test_result_fail("KSM not enabled\n"); +				test_child_ksm_err(status);  				return;  			}  		} else { @@ -545,7 +608,7 @@ static void test_prctl_unmerge(void)  	ksft_print_msg("[RUN] %s\n", __func__); -	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, true); +	map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, KSM_MERGE_PRCTL);  	if (map == MAP_FAILED)  		return; @@ -568,7 +631,7 @@ static void test_prot_none(void)  	ksft_print_msg("[RUN] %s\n", __func__); -	map = mmap_and_merge_range(0x11, size, PROT_NONE, false); +	map = mmap_and_merge_range(0x11, size, PROT_NONE, KSM_MERGE_MADVISE);  	if (map == MAP_FAILED)  		goto unmap; @@ -599,7 +662,7 @@ int main(int argc, char **argv)  	int err;  	if (argc > 1 && !strcmp(argv[1], FORK_EXEC_CHILD_PRG_NAME)) { -		exit(ksm_fork_exec_child() == 1 ? 0 : 1); +		exit(test_child_ksm());  	}  #ifdef __NR_userfaultfd @@ -646,5 +709,5 @@ int main(int argc, char **argv)  	if (err)  		ksft_exit_fail_msg("%d out of %d tests failed\n",  				   err, ksft_test_num()); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/madv_populate.c b/tools/testing/selftests/mm/madv_populate.c index 17bcb07f19f3..ef7d911da13e 100644 --- a/tools/testing/selftests/mm/madv_populate.c +++ b/tools/testing/selftests/mm/madv_populate.c @@ -307,5 +307,5 @@ int main(int argc, char **argv)  	if (err)  		ksft_exit_fail_msg("%d out of %d tests failed\n",  				   err, ksft_test_num()); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/mdwe_test.c b/tools/testing/selftests/mm/mdwe_test.c index 200bedcdc32e..1e01d3ddc11c 100644 --- a/tools/testing/selftests/mm/mdwe_test.c +++ b/tools/testing/selftests/mm/mdwe_test.c @@ -7,6 +7,7 @@  #include <linux/mman.h>  #include <linux/prctl.h> +#define _GNU_SOURCE  #include <stdio.h>  #include <stdlib.h>  #include <sys/auxv.h> diff --git a/tools/testing/selftests/mm/memfd_secret.c b/tools/testing/selftests/mm/memfd_secret.c index 9b298f6a04b3..9a0597310a76 100644 --- a/tools/testing/selftests/mm/memfd_secret.c +++ b/tools/testing/selftests/mm/memfd_secret.c @@ -20,6 +20,7 @@  #include <unistd.h>  #include <errno.h>  #include <stdio.h> +#include <fcntl.h>  #include "../kselftest.h" @@ -83,6 +84,45 @@ static void test_mlock_limit(int fd)  	pass("mlock limit is respected\n");  } +static void test_vmsplice(int fd, const char *desc) +{ +	ssize_t transferred; +	struct iovec iov; +	int pipefd[2]; +	char *mem; + +	if (pipe(pipefd)) { +		fail("pipe failed: %s\n", strerror(errno)); +		return; +	} + +	mem = mmap(NULL, page_size, prot, mode, fd, 0); +	if (mem == MAP_FAILED) { +		fail("Unable to mmap secret memory\n"); +		goto close_pipe; +	} + +	/* +	 * vmsplice() may use GUP-fast, which must also fail. Prefault the +	 * page table, so GUP-fast could find it. +	 */ +	memset(mem, PATTERN, page_size); + +	iov.iov_base = mem; +	iov.iov_len = page_size; +	transferred = vmsplice(pipefd[1], &iov, 1, 0); + +	if (transferred < 0 && errno == EFAULT) +		pass("vmsplice is blocked as expected with %s\n", desc); +	else +		fail("vmsplice: unexpected memory access with %s\n", desc); + +	munmap(mem, page_size); +close_pipe: +	close(pipefd[0]); +	close(pipefd[1]); +} +  static void try_process_vm_read(int fd, int pipefd[2])  {  	struct iovec liov, riov; @@ -187,7 +227,6 @@ static void test_remote_access(int fd, const char *name,  		return;  	} -	ftruncate(fd, page_size);  	memset(mem, PATTERN, page_size);  	if (write(pipefd[1], &mem, sizeof(mem)) < 0) { @@ -258,7 +297,7 @@ static void prepare(void)  				   strerror(errno));  } -#define NUM_TESTS 4 +#define NUM_TESTS 6  int main(int argc, char *argv[])  { @@ -277,9 +316,17 @@ int main(int argc, char *argv[])  			ksft_exit_fail_msg("memfd_secret failed: %s\n",  					   strerror(errno));  	} +	if (ftruncate(fd, page_size)) +		ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno));  	test_mlock_limit(fd);  	test_file_apis(fd); +	/* +	 * We have to run the first vmsplice test before any secretmem page was +	 * allocated for this fd. +	 */ +	test_vmsplice(fd, "fresh page"); +	test_vmsplice(fd, "existing page");  	test_process_vm_read(fd);  	test_ptrace(fd); diff --git a/tools/testing/selftests/mm/mkdirty.c b/tools/testing/selftests/mm/mkdirty.c index 301abb99e027..b8a7efe9204e 100644 --- a/tools/testing/selftests/mm/mkdirty.c +++ b/tools/testing/selftests/mm/mkdirty.c @@ -375,5 +375,5 @@ int main(void)  	if (err)  		ksft_exit_fail_msg("%d out of %d tests failed\n",  				   err, ksft_test_num()); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/mlock2-tests.c b/tools/testing/selftests/mm/mlock2-tests.c index 26f744188ad0..7f0d50fa361d 100644 --- a/tools/testing/selftests/mm/mlock2-tests.c +++ b/tools/testing/selftests/mm/mlock2-tests.c @@ -20,8 +20,6 @@ static int get_vm_area(unsigned long addr, struct vm_boundaries *area)  	FILE *file;  	int ret = 1;  	char line[1024] = {0}; -	char *end_addr; -	char *stop;  	unsigned long start;  	unsigned long end; @@ -37,21 +35,10 @@ static int get_vm_area(unsigned long addr, struct vm_boundaries *area)  	memset(area, 0, sizeof(struct vm_boundaries));  	while(fgets(line, 1024, file)) { -		end_addr = strchr(line, '-'); -		if (!end_addr) { +		if (sscanf(line, "%lx-%lx", &start, &end) != 2) {  			ksft_print_msg("cannot parse /proc/self/maps\n");  			goto out;  		} -		*end_addr = '\0'; -		end_addr++; -		stop = strchr(end_addr, ' '); -		if (!stop) { -			ksft_print_msg("cannot parse /proc/self/maps\n"); -			goto out; -		} - -		sscanf(line, "%lx", &start); -		sscanf(end_addr, "%lx", &end);  		if (start <= addr && end > addr) {  			area->start = start; diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c index 2f8b991f78cb..1b03bcfaefdf 100644 --- a/tools/testing/selftests/mm/mremap_test.c +++ b/tools/testing/selftests/mm/mremap_test.c @@ -23,6 +23,7 @@  #define VALIDATION_NO_THRESHOLD 0	/* Verify the entire region */  #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))  #define SIZE_MB(m) ((size_t)m * (1024 * 1024))  #define SIZE_KB(k) ((size_t)k * 1024) @@ -69,6 +70,27 @@ enum {  	.expect_failure = should_fail				\  } +/* compute square root using binary search */ +static unsigned long get_sqrt(unsigned long val) +{ +	unsigned long low = 1; + +	/* assuming rand_size is less than 1TB */ +	unsigned long high = (1UL << 20); + +	while (low <= high) { +		unsigned long mid = low + (high - low) / 2; +		unsigned long temp = mid * mid; + +		if (temp == val) +			return mid; +		if (temp < val) +			low = mid + 1; +		high = mid - 1; +	} +	return low; +} +  /*   * Returns false if the requested remap region overlaps with an   * existing mapping (e.g text, stack) else returns true. @@ -126,19 +148,21 @@ static unsigned long long get_mmap_min_addr(void)   * Using /proc/self/maps, assert that the specified address range is contained   * within a single mapping.   */ -static bool is_range_mapped(FILE *maps_fp, void *start, void *end) +static bool is_range_mapped(FILE *maps_fp, unsigned long start, +			    unsigned long end)  {  	char *line = NULL;  	size_t len = 0;  	bool success = false; +	unsigned long first_val, second_val;  	rewind(maps_fp);  	while (getline(&line, &len, maps_fp) != -1) { -		char *first = strtok(line, "- "); -		void *first_val = (void *)strtol(first, NULL, 16); -		char *second = strtok(NULL, "- "); -		void *second_val = (void *) strtol(second, NULL, 16); +		if (sscanf(line, "%lx-%lx", &first_val, &second_val) != 2) { +			ksft_exit_fail_msg("cannot parse /proc/self/maps\n"); +			break; +		}  		if (first_val <= start && second_val >= end) {  			success = true; @@ -233,7 +257,8 @@ static void mremap_expand_merge(FILE *maps_fp, unsigned long page_size)  		goto out;  	} -	success = is_range_mapped(maps_fp, start, start + 3 * page_size); +	success = is_range_mapped(maps_fp, (unsigned long)start, +				  (unsigned long)(start + 3 * page_size));  	munmap(start, 3 * page_size);  out: @@ -272,7 +297,8 @@ static void mremap_expand_merge_offset(FILE *maps_fp, unsigned long page_size)  		goto out;  	} -	success = is_range_mapped(maps_fp, start, start + 3 * page_size); +	success = is_range_mapped(maps_fp, (unsigned long)start, +				  (unsigned long)(start + 3 * page_size));  	munmap(start, 3 * page_size);  out: @@ -296,7 +322,7 @@ out:   *   * |DDDDddddSSSSssss|   */ -static void mremap_move_within_range(char pattern_seed) +static void mremap_move_within_range(unsigned int pattern_seed, char *rand_addr)  {  	char *test_name = "mremap mremap move within range";  	void *src, *dest; @@ -316,10 +342,7 @@ static void mremap_move_within_range(char pattern_seed)  	src = (void *)((unsigned long)src & ~(SIZE_MB(2) - 1));  	/* Set byte pattern for source block. */ -	srand(pattern_seed); -	for (i = 0; i < SIZE_MB(2); i++) { -		((char *)src)[i] = (char) rand(); -	} +	memcpy(src, rand_addr, SIZE_MB(2));  	dest = src - SIZE_MB(2); @@ -357,14 +380,14 @@ out:  /* Returns the time taken for the remap on success else returns -1. */  static long long remap_region(struct config c, unsigned int threshold_mb, -			      char pattern_seed) +			      char *rand_addr)  {  	void *addr, *src_addr, *dest_addr, *dest_preamble_addr; -	int d; -	unsigned long long t; +	unsigned long long t, d;  	struct timespec t_start = {0, 0}, t_end = {0, 0};  	long long  start_ns, end_ns, align_mask, ret, offset;  	unsigned long long threshold; +	unsigned long num_chunks;  	if (threshold_mb == VALIDATION_NO_THRESHOLD)  		threshold = c.region_size; @@ -378,9 +401,7 @@ static long long remap_region(struct config c, unsigned int threshold_mb,  	}  	/* Set byte pattern for source block. */ -	srand(pattern_seed); -	for (t = 0; t < threshold; t++) -		memset((char *) src_addr + t, (char) rand(), 1); +	memcpy(src_addr, rand_addr, threshold);  	/* Mask to zero out lower bits of address for alignment */  	align_mask = ~(c.dest_alignment - 1); @@ -420,9 +441,7 @@ static long long remap_region(struct config c, unsigned int threshold_mb,  		}  		/* Set byte pattern for the dest preamble block. */ -		srand(pattern_seed); -		for (d = 0; d < c.dest_preamble_size; d++) -			memset((char *) dest_preamble_addr + d, (char) rand(), 1); +		memcpy(dest_preamble_addr, rand_addr, c.dest_preamble_size);  	}  	clock_gettime(CLOCK_MONOTONIC, &t_start); @@ -436,15 +455,42 @@ static long long remap_region(struct config c, unsigned int threshold_mb,  		goto clean_up_dest_preamble;  	} -	/* Verify byte pattern after remapping */ -	srand(pattern_seed); -	for (t = 0; t < threshold; t++) { -		char c = (char) rand(); +	/* +	 * Verify byte pattern after remapping. Employ an algorithm with a +	 * square root time complexity in threshold: divide the range into +	 * chunks, if memcmp() returns non-zero, only then perform an +	 * iteration in that chunk to find the mismatch index. +	 */ +	num_chunks = get_sqrt(threshold); +	for (unsigned long i = 0; i < num_chunks; ++i) { +		size_t chunk_size = threshold / num_chunks; +		unsigned long shift = i * chunk_size; + +		if (!memcmp(dest_addr + shift, rand_addr + shift, chunk_size)) +			continue; + +		/* brute force iteration only over mismatch segment */ +		for (t = shift; t < shift + chunk_size; ++t) { +			if (((char *) dest_addr)[t] != rand_addr[t]) { +				ksft_print_msg("Data after remap doesn't match at offset %llu\n", +						t); +				ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[t] & 0xff, +						((char *) dest_addr)[t] & 0xff); +				ret = -1; +				goto clean_up_dest; +			} +		} +	} -		if (((char *) dest_addr)[t] != c) { +	/* +	 * if threshold is not divisible by num_chunks, then check the +	 * last chunk +	 */ +	for (t = num_chunks * (threshold / num_chunks); t < threshold; ++t) { +		if (((char *) dest_addr)[t] != rand_addr[t]) {  			ksft_print_msg("Data after remap doesn't match at offset %llu\n", -				       t); -			ksft_print_msg("Expected: %#x\t Got: %#x\n", c & 0xff, +					t); +			ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[t] & 0xff,  					((char *) dest_addr)[t] & 0xff);  			ret = -1;  			goto clean_up_dest; @@ -452,22 +498,44 @@ static long long remap_region(struct config c, unsigned int threshold_mb,  	}  	/* Verify the dest preamble byte pattern after remapping */ -	if (c.dest_preamble_size) { -		srand(pattern_seed); -		for (d = 0; d < c.dest_preamble_size; d++) { -			char c = (char) rand(); +	if (!c.dest_preamble_size) +		goto no_preamble; + +	num_chunks = get_sqrt(c.dest_preamble_size); -			if (((char *) dest_preamble_addr)[d] != c) { -				ksft_print_msg("Preamble data after remap doesn't match at offset %d\n", -					       d); -				ksft_print_msg("Expected: %#x\t Got: %#x\n", c & 0xff, -					       ((char *) dest_preamble_addr)[d] & 0xff); +	for (unsigned long i = 0; i < num_chunks; ++i) { +		size_t chunk_size = c.dest_preamble_size / num_chunks; +		unsigned long shift = i * chunk_size; + +		if (!memcmp(dest_preamble_addr + shift, rand_addr + shift, +			    chunk_size)) +			continue; + +		/* brute force iteration only over mismatched segment */ +		for (d = shift; d < shift + chunk_size; ++d) { +			if (((char *) dest_preamble_addr)[d] != rand_addr[d]) { +				ksft_print_msg("Preamble data after remap doesn't match at offset %llu\n", +						d); +				ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[d] & 0xff, +						((char *) dest_preamble_addr)[d] & 0xff);  				ret = -1;  				goto clean_up_dest;  			}  		}  	} +	for (d = num_chunks * (c.dest_preamble_size / num_chunks); d < c.dest_preamble_size; ++d) { +		if (((char *) dest_preamble_addr)[d] != rand_addr[d]) { +			ksft_print_msg("Preamble data after remap doesn't match at offset %llu\n", +					d); +			ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[d] & 0xff, +					((char *) dest_preamble_addr)[d] & 0xff); +			ret = -1; +			goto clean_up_dest; +		} +	} + +no_preamble:  	start_ns = t_start.tv_sec * NS_PER_SEC + t_start.tv_nsec;  	end_ns = t_end.tv_sec * NS_PER_SEC + t_end.tv_nsec;  	ret = end_ns - start_ns; @@ -494,7 +562,8 @@ out:   * the beginning of the mapping just because the aligned   * down address landed on a mapping that maybe does not exist.   */ -static void mremap_move_1mb_from_start(char pattern_seed) +static void mremap_move_1mb_from_start(unsigned int pattern_seed, +				       char *rand_addr)  {  	char *test_name = "mremap move 1mb from start at 1MB+256KB aligned src";  	void *src = NULL, *dest = NULL; @@ -520,10 +589,7 @@ static void mremap_move_1mb_from_start(char pattern_seed)  	}  	/* Set byte pattern for source block. */ -	srand(pattern_seed); -	for (i = 0; i < SIZE_MB(2); i++) { -		((char *)src)[i] = (char) rand(); -	} +	memcpy(src, rand_addr, SIZE_MB(2));  	/*  	 * Unmap the beginning of dest so that the aligned address @@ -568,10 +634,10 @@ out:  static void run_mremap_test_case(struct test test_case, int *failures,  				 unsigned int threshold_mb, -				 unsigned int pattern_seed) +				 unsigned int pattern_seed, char *rand_addr)  {  	long long remap_time = remap_region(test_case.config, threshold_mb, -					    pattern_seed); +					    rand_addr);  	if (remap_time < 0) {  		if (test_case.expect_failure) @@ -642,7 +708,15 @@ int main(int argc, char **argv)  	int failures = 0;  	int i, run_perf_tests;  	unsigned int threshold_mb = VALIDATION_DEFAULT_THRESHOLD; + +	/* hard-coded test configs */ +	size_t max_test_variable_region_size = _2GB; +	size_t max_test_constant_region_size = _2MB; +	size_t dest_preamble_size = 10 * _4MB; +  	unsigned int pattern_seed; +	char *rand_addr; +	size_t rand_size;  	int num_expand_tests = 2;  	int num_misc_tests = 2;  	struct test test_cases[MAX_TEST] = {}; @@ -659,6 +733,31 @@ int main(int argc, char **argv)  	ksft_print_msg("Test configs:\n\tthreshold_mb=%u\n\tpattern_seed=%u\n\n",  		       threshold_mb, pattern_seed); +	/* +	 * set preallocated random array according to test configs; see the +	 * functions for the logic of setting the size +	 */ +	if (!threshold_mb) +		rand_size = MAX(max_test_variable_region_size, +				max_test_constant_region_size); +	else +		rand_size = MAX(MIN(threshold_mb * _1MB, +				    max_test_variable_region_size), +				max_test_constant_region_size); +	rand_size = MAX(dest_preamble_size, rand_size); + +	rand_addr = (char *)mmap(NULL, rand_size, PROT_READ | PROT_WRITE, +				 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +	if (rand_addr == MAP_FAILED) { +		perror("mmap"); +		ksft_exit_fail_msg("cannot mmap rand_addr\n"); +	} + +	/* fill stream of random bytes */ +	srand(pattern_seed); +	for (unsigned long i = 0; i < rand_size; ++i) +		rand_addr[i] = (char) rand(); +  	page_size = sysconf(_SC_PAGESIZE);  	/* Expected mremap failures */ @@ -730,13 +829,13 @@ int main(int argc, char **argv)  	for (i = 0; i < ARRAY_SIZE(test_cases); i++)  		run_mremap_test_case(test_cases[i], &failures, threshold_mb, -				     pattern_seed); +				     pattern_seed, rand_addr);  	maps_fp = fopen("/proc/self/maps", "r");  	if (maps_fp == NULL) { -		ksft_print_msg("Failed to read /proc/self/maps: %s\n", strerror(errno)); -		exit(KSFT_FAIL); +		munmap(rand_addr, rand_size); +		ksft_exit_fail_msg("Failed to read /proc/self/maps: %s\n", strerror(errno));  	}  	mremap_expand_merge(maps_fp, page_size); @@ -744,17 +843,20 @@ int main(int argc, char **argv)  	fclose(maps_fp); -	mremap_move_within_range(pattern_seed); -	mremap_move_1mb_from_start(pattern_seed); +	mremap_move_within_range(pattern_seed, rand_addr); +	mremap_move_1mb_from_start(pattern_seed, rand_addr);  	if (run_perf_tests) {  		ksft_print_msg("\n%s\n",  		 "mremap HAVE_MOVE_PMD/PUD optimization time comparison for 1GB region:");  		for (i = 0; i < ARRAY_SIZE(perf_test_cases); i++)  			run_mremap_test_case(perf_test_cases[i], &failures, -					     threshold_mb, pattern_seed); +					     threshold_mb, pattern_seed, +					     rand_addr);  	} +	munmap(rand_addr, rand_size); +  	if (failures > 0)  		ksft_exit_fail();  	else diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index d59517ed3d48..2d785aca72a5 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1484,7 +1484,7 @@ int main(int argc, char *argv[])  	ksft_print_header();  	if (init_uffd()) -		return ksft_exit_pass(); +		ksft_exit_pass();  	ksft_set_plan(115); @@ -1660,5 +1660,5 @@ int main(int argc, char *argv[])  	userfaultfd_tests();  	close(pagemap_fd); -	return ksft_exit_pass(); +	ksft_exit_pass();  } diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c index 374a308174d2..48dc151f8fca 100644 --- a/tools/testing/selftests/mm/protection_keys.c +++ b/tools/testing/selftests/mm/protection_keys.c @@ -54,7 +54,6 @@ int test_nr;  u64 shadow_pkey_reg;  int dprint_in_signal;  char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE]; -char buf[256];  void cat_into_file(char *str, char *file)  { @@ -1745,42 +1744,6 @@ void pkey_setup_shadow(void)  	shadow_pkey_reg = __read_pkey_reg();  } -pid_t parent_pid; - -void restore_settings_atexit(void) -{ -	if (parent_pid == getpid()) -		cat_into_file(buf, "/proc/sys/vm/nr_hugepages"); -} - -void save_settings(void) -{ -	int fd; -	int err; - -	if (geteuid()) -		return; - -	fd = open("/proc/sys/vm/nr_hugepages", O_RDONLY); -	if (fd < 0) { -		fprintf(stderr, "error opening\n"); -		perror("error: "); -		exit(__LINE__); -	} - -	/* -1 to guarantee leaving the trailing \0 */ -	err = read(fd, buf, sizeof(buf)-1); -	if (err < 0) { -		fprintf(stderr, "error reading\n"); -		perror("error: "); -		exit(__LINE__); -	} - -	parent_pid = getpid(); -	atexit(restore_settings_atexit); -	close(fd); -} -  int main(void)  {  	int nr_iterations = 22; @@ -1788,7 +1751,6 @@ int main(void)  	srand((unsigned int)time(NULL)); -	save_settings();  	setup_handlers();  	printf("has pkeys: %d\n", pkeys_supported); diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index c2c542fe7b17..3157204b9047 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -152,9 +152,13 @@ done < /proc/meminfo  # both of these requirements into account and attempt to increase  # number of huge pages available.  nr_cpus=$(nproc) -hpgsize_MB=$((hpgsize_KB / 1024)) -half_ufd_size_MB=$((((nr_cpus * hpgsize_MB + 127) / 128) * 128)) -needmem_KB=$((half_ufd_size_MB * 2 * 1024)) +uffd_min_KB=$((hpgsize_KB * nr_cpus * 2)) +hugetlb_min_KB=$((256 * 1024)) +if [[ $uffd_min_KB -gt $hugetlb_min_KB ]]; then +	needmem_KB=$uffd_min_KB +else +	needmem_KB=$hugetlb_min_KB +fi  # set proper nr_hugepages  if [ -n "$freepgs" ] && [ -n "$hpgsize_KB" ]; then @@ -294,7 +298,8 @@ CATEGORY="userfaultfd" run_test ./uffd-unit-tests  uffd_stress_bin=./uffd-stress  CATEGORY="userfaultfd" run_test ${uffd_stress_bin} anon 20 16  # Hugetlb tests require source and destination huge pages. Pass in half -# the size ($half_ufd_size_MB), which is used for *each*. +# the size of the free pages we have, which is used for *each*. +half_ufd_size_MB=$((freepgs / 2))  CATEGORY="userfaultfd" run_test ${uffd_stress_bin} hugetlb "$half_ufd_size_MB" 32  CATEGORY="userfaultfd" run_test ${uffd_stress_bin} hugetlb-private "$half_ufd_size_MB" 32  CATEGORY="userfaultfd" run_test ${uffd_stress_bin} shmem 20 16 @@ -385,6 +390,7 @@ CATEGORY="ksm_numa" run_test ./ksm_tests -N -m 0  CATEGORY="ksm" run_test ./ksm_functional_tests  # protection_keys tests +nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages)  if [ -x ./protection_keys_32 ]  then  	CATEGORY="pkey" run_test ./protection_keys_32 @@ -394,6 +400,7 @@ if [ -x ./protection_keys_64 ]  then  	CATEGORY="pkey" run_test ./protection_keys_64  fi +echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages  if [ -x ./soft-dirty ]  then diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c index 7dbfa53d93a0..bdfa5d085f00 100644 --- a/tools/testing/selftests/mm/soft-dirty.c +++ b/tools/testing/selftests/mm/soft-dirty.c @@ -209,5 +209,5 @@ int main(int argc, char **argv)  	close(pagemap_fd); -	return ksft_exit_pass(); +	ksft_finished();  } diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 6c988bd2f335..d3c7f5fb3e7b 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -300,7 +300,7 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd,  		char **addr)  {  	size_t i; -	int dummy; +	int __attribute__((unused)) dummy = 0;  	srand(time(NULL)); diff --git a/tools/testing/selftests/mm/virtual_address_range.c b/tools/testing/selftests/mm/virtual_address_range.c index 7bcf8d48256a..4e4c1e311247 100644 --- a/tools/testing/selftests/mm/virtual_address_range.c +++ b/tools/testing/selftests/mm/virtual_address_range.c @@ -12,6 +12,8 @@  #include <errno.h>  #include <sys/mman.h>  #include <sys/time.h> +#include <fcntl.h> +  #include "../kselftest.h"  /* @@ -85,7 +87,7 @@ static int validate_lower_address_hint(void)  	char *ptr;  	ptr = mmap((void *) (1UL << 45), MAP_CHUNK_SIZE, PROT_READ | -			PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +		   PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);  	if (ptr == MAP_FAILED)  		return 0; @@ -93,6 +95,66 @@ static int validate_lower_address_hint(void)  	return 1;  } +static int validate_complete_va_space(void) +{ +	unsigned long start_addr, end_addr, prev_end_addr; +	char line[400]; +	char prot[6]; +	FILE *file; +	int fd; + +	fd = open("va_dump", O_CREAT | O_WRONLY, 0600); +	unlink("va_dump"); +	if (fd < 0) { +		ksft_test_result_skip("cannot create or open dump file\n"); +		ksft_finished(); +	} + +	file = fopen("/proc/self/maps", "r"); +	if (file == NULL) +		ksft_exit_fail_msg("cannot open /proc/self/maps\n"); + +	prev_end_addr = 0; +	while (fgets(line, sizeof(line), file)) { +		unsigned long hop; + +		if (sscanf(line, "%lx-%lx %s[rwxp-]", +			   &start_addr, &end_addr, prot) != 3) +			ksft_exit_fail_msg("cannot parse /proc/self/maps\n"); + +		/* end of userspace mappings; ignore vsyscall mapping */ +		if (start_addr & (1UL << 63)) +			return 0; + +		/* /proc/self/maps must have gaps less than MAP_CHUNK_SIZE */ +		if (start_addr - prev_end_addr >= MAP_CHUNK_SIZE) +			return 1; + +		prev_end_addr = end_addr; + +		if (prot[0] != 'r') +			continue; + +		/* +		 * Confirm whether MAP_CHUNK_SIZE chunk can be found or not. +		 * If write succeeds, no need to check MAP_CHUNK_SIZE - 1 +		 * addresses after that. If the address was not held by this +		 * process, write would fail with errno set to EFAULT. +		 * Anyways, if write returns anything apart from 1, exit the +		 * program since that would mean a bug in /proc/self/maps. +		 */ +		hop = 0; +		while (start_addr + hop < end_addr) { +			if (write(fd, (void *)(start_addr + hop), 1) != 1) +				return 1; +			lseek(fd, 0, SEEK_SET); + +			hop += MAP_CHUNK_SIZE; +		} +	} +	return 0; +} +  int main(int argc, char *argv[])  {  	char *ptr[NR_CHUNKS_LOW]; @@ -105,13 +167,11 @@ int main(int argc, char *argv[])  	for (i = 0; i < NR_CHUNKS_LOW; i++) {  		ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE, -					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +			      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);  		if (ptr[i] == MAP_FAILED) { -			if (validate_lower_address_hint()) { -				ksft_test_result_skip("Memory constraint not fulfilled\n"); -				ksft_finished(); -			} +			if (validate_lower_address_hint()) +				ksft_exit_fail_msg("mmap unexpectedly succeeded with hint\n");  			break;  		} @@ -127,7 +187,7 @@ int main(int argc, char *argv[])  	for (i = 0; i < NR_CHUNKS_HIGH; i++) {  		hint = hind_addr();  		hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE, -					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +			       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);  		if (hptr[i] == MAP_FAILED)  			break; @@ -135,6 +195,10 @@ int main(int argc, char *argv[])  		validate_addr(hptr[i], 1);  	}  	hchunks = i; +	if (validate_complete_va_space()) { +		ksft_test_result_fail("BUG in mmap() or /proc/self/maps\n"); +		ksft_finished(); +	}  	for (i = 0; i < lchunks; i++)  		munmap(ptr[i], MAP_CHUNK_SIZE);  | 
