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