• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *   Copyright (c) 2008 Vijay Kumar B. <vijaykumar@bravegnu.org>
4  *   Copyright (c) International Business Machines  Corp., 2001
5  */
6 
7 /*\
8  * [Description]
9  *
10  * Verify that move_pages() properly reports failures when the memory area is
11  * not valid, no page is mapped yet or the shared zero page is mapped.
12  *
13  * [Algorithm]
14  *
15  *      1. Pass the address of a valid memory area where no page is
16  *         mapped yet (not read/written), the address of a valid memory area
17  *         where the shared zero page is mapped (read, but not written to)
18  *         and the address of an invalid memory area as page addresses to
19  *         move_pages().
20  *      2. Check if the corresponding status for "no page mapped" is set to
21  *         -ENOENT. Note that kernels >= 4.3 [1] and < 6.12 [2] wrongly returned
22  *         -EFAULT by accident.
23  *      3. Check if the corresponding status for "shared zero page" is set to:
24  *         -EFAULT. Note that kernels < 4.3 [1] wrongly returned -ENOENT.
25  *      4. Check if the corresponding status for "invalid memory area" is set
26  *         to -EFAULT.
27  *
28  *   [1] d899844e9c98 "mm: fix status code which move_pages() returns for zero page"
29  *   [2] 7dff875c9436 "mm/migrate: convert add_page_for_migration() from follow_page() to folio_walk"
30  */
31 
32 #include <sys/mman.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include "tst_test.h"
39 #include "move_pages_support.h"
40 
41 #define TEST_PAGES 4
42 #define TEST_NODES 2
43 #define TOUCHED_PAGES 1
44 #define NO_PAGE TOUCHED_PAGES
45 #define ZERO_PAGE (NO_PAGE + 1)
46 #define INVALID_PAGE (ZERO_PAGE + 1)
47 
run(void)48 static void run(void)
49 {
50 #ifdef HAVE_NUMA_V2
51 	unsigned int i;
52 	unsigned int from_node;
53 	unsigned int to_node;
54 	int ret;
55 	void *pages[TEST_PAGES] = { 0 };
56 	int nodes[TEST_PAGES];
57 	int status[TEST_PAGES];
58 	unsigned long onepage = get_page_size();
59 	char tmp;
60 
61 	ret = get_allowed_nodes(NH_MEMS, 2, &from_node, &to_node);
62 	if (ret < 0)
63 		tst_brk(TBROK | TERRNO, "get_allowed_nodes: %d", ret);
64 
65 	ret = alloc_pages_on_node(pages, TOUCHED_PAGES, from_node);
66 	if (ret == -1)
67 		tst_brk(TBROK, "failed allocating memory on node %d",
68 			from_node);
69 
70 	/*
71 	 * Allocate memory and do not touch it. Consequently, no
72 	 * page will be faulted in / mapped into the page tables.
73 	 */
74 	pages[NO_PAGE] = numa_alloc_onnode(onepage, from_node);
75 	if (pages[NO_PAGE] == NULL)
76 		tst_brk(TBROK, "failed allocating memory on node %d",
77 			from_node);
78 
79 	/*
80 	 * Allocate memory, read from it, but do not write to it. This
81 	 * will populate the shared zeropage.
82 	 */
83 	pages[ZERO_PAGE] = numa_alloc_onnode(onepage, from_node);
84 	if (pages[ZERO_PAGE] == NULL)
85 		tst_brk(TBROK, "failed allocating memory on node %d",
86 			from_node);
87 	/* Make the compiler not optimize-out the read. */
88 	tmp = *((char *)pages[ZERO_PAGE]);
89 	asm volatile("" : "+r" (tmp));
90 
91 	/*
92 	 * Temporarily allocate memory and free it immediately. Do this
93 	 * as the last step so the area won't get reused before we're
94 	 * done.
95 	 */
96 	pages[INVALID_PAGE] = numa_alloc_onnode(onepage, from_node);
97 	if (pages[INVALID_PAGE] == NULL)
98 		tst_brk(TBROK, "failed allocating memory on node %d",
99 			from_node);
100 	numa_free(pages[INVALID_PAGE], onepage);
101 
102 	for (i = 0; i < TEST_PAGES; i++)
103 		nodes[i] = to_node;
104 
105 	ret = numa_move_pages(0, TEST_PAGES, pages, nodes,
106 			      status, MPOL_MF_MOVE);
107 	if (ret == -1) {
108 		tst_res(TFAIL | TERRNO,
109 			"move_pages unexpectedly failed");
110 		goto err_free_pages;
111 	} else if (ret > 0) {
112 		tst_res(TINFO, "move_pages() returned %d", ret);
113 	}
114 
115 	if (status[NO_PAGE] == -ENOENT) {
116 		tst_res(TPASS, "status[%d] has expected value",
117 			NO_PAGE);
118 	} else {
119 		tst_res(TFAIL, "status[%d] is %s, expected %s",
120 			NO_PAGE,
121 			tst_strerrno(-status[NO_PAGE]),
122 			tst_strerrno(ENOENT));
123 	}
124 
125 	if (status[ZERO_PAGE] == -EFAULT) {
126 		tst_res(TPASS, "status[%d] has expected value",
127 			ZERO_PAGE);
128 	} else {
129 		tst_res(TFAIL, "status[%d] is %s, expected %s",
130 			ZERO_PAGE,
131 			tst_strerrno(-status[ZERO_PAGE]),
132 			tst_strerrno(EFAULT));
133 	}
134 
135 	if (status[INVALID_PAGE] == -EFAULT) {
136 		tst_res(TPASS, "status[%d] has expected value",
137 			INVALID_PAGE);
138 	} else {
139 		tst_res(TFAIL, "status[%d] is %s, expected %s",
140 			INVALID_PAGE,
141 			tst_strerrno(-status[INVALID_PAGE]),
142 			tst_strerrno(EFAULT));
143 	}
144 
145 err_free_pages:
146 	/* Memory for the invalid page was already freed. */
147 	pages[INVALID_PAGE] = NULL;
148 	/* This is capable of freeing all memory we allocated. */
149 	free_pages(pages, TEST_PAGES);
150 #else
151 	tst_res(TCONF, NUMA_ERROR_MSG);
152 #endif
153 }
154 
setup(void)155 void setup(void)
156 {
157 	check_config(TEST_NODES);
158 }
159 
160 static struct tst_test test = {
161 	.test_all = run,
162 	.setup = setup,
163 	.tags = (const struct tst_tag[]) {
164 		{"linux-git", "d899844e9c98"},
165 		{"linux-git", "7dff875c9436"},
166 		{}
167 	}
168 };
169