1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 Cyril Hrubis <[email protected]>
4 */
5
6 /*
7 * We are testing mbind() MPOL_MF_MOVE and MPOL_MF_MOVE_ALL.
8 *
9 * If one of these flags is passed along with the policy kernel attempts to
10 * move already faulted pages to match the requested policy.
11 */
12
13 #include <errno.h>
14 #include "config.h"
15 #ifdef HAVE_NUMA_H
16 # include <numa.h>
17 # include <numaif.h>
18 # include "mbind.h"
19 #endif
20 #include "tst_test.h"
21 #include "tst_numa.h"
22
23 #ifdef HAVE_NUMA_V2
24
25 static size_t page_size;
26 static struct tst_nodemap *nodes;
27
setup(void)28 static void setup(void)
29 {
30 page_size = getpagesize();
31
32 nodes = tst_get_nodemap(TST_NUMA_MEM, 2 * page_size / 1024);
33 if (nodes->cnt <= 1)
34 tst_brk(TCONF, "Test requires at least two NUMA memory nodes");
35 }
36
cleanup(void)37 static void cleanup(void)
38 {
39 tst_nodemap_free(nodes);
40 }
41
verify_policy(int mode,unsigned flag)42 static void verify_policy(int mode, unsigned flag)
43 {
44 struct bitmask *bm = numa_allocate_nodemask();
45 unsigned int i;
46 void *ptr;
47 unsigned long size = page_size;
48 unsigned int node = 0;
49
50 ptr = tst_numa_map(NULL, size);
51 tst_nodemap_reset_counters(nodes);
52 tst_numa_fault(ptr, size);
53 tst_nodemap_count_pages(nodes, ptr, size);
54 tst_nodemap_print_counters(nodes);
55
56 for (i = 0; i < nodes->cnt; i++) {
57 if (!nodes->counters[i]) {
58 node = nodes->map[i];
59 tst_res(TINFO, "Attempting to move to node %i", node);
60 numa_bitmask_setbit(bm, node);
61 break;
62 }
63 }
64
65 TEST(mbind(ptr, size, mode, bm->maskp, bm->size + 1, flag));
66
67 if (TST_RET) {
68 tst_res(TFAIL | TTERRNO,
69 "mbind(%s, %s) node %u",
70 tst_mempolicy_mode_name(mode), mbind_flag_name(flag), node);
71 goto exit;
72 } else {
73 tst_res(TPASS, "mbind(%s, %s) node %u succeded",
74 tst_mempolicy_mode_name(mode), mbind_flag_name(flag), node);
75 }
76
77 tst_nodemap_reset_counters(nodes);
78 tst_nodemap_count_pages(nodes, ptr, size);
79
80 for (i = 0; i < nodes->cnt; i++) {
81 if (nodes->map[i] == node) {
82 if (nodes->counters[i] == 1) {
83 tst_res(TPASS, "Node %u allocated %u", node, 1);
84 } else {
85 tst_res(TFAIL, "Node %u allocated %u, expected %u",
86 node, nodes->counters[i], 0);
87 }
88 continue;
89 }
90
91 if (nodes->counters[i]) {
92 tst_res(TFAIL, "Node %u allocated %u, expected 0",
93 i, nodes->counters[i]);
94 }
95 }
96
97 exit:
98 tst_numa_unmap(ptr, size);
99 numa_free_nodemask(bm);
100 }
101
102 static const int modes[] = {
103 MPOL_PREFERRED,
104 MPOL_BIND,
105 MPOL_INTERLEAVE,
106 };
107
verify_mbind(unsigned int n)108 static void verify_mbind(unsigned int n)
109 {
110 verify_policy(modes[n], MPOL_MF_MOVE);
111 verify_policy(modes[n], MPOL_MF_MOVE_ALL);
112 }
113
114 static struct tst_test test = {
115 .setup = setup,
116 .cleanup = cleanup,
117 .test = verify_mbind,
118 .tcnt = ARRAY_SIZE(modes),
119 .needs_root = 1,
120 };
121
122 #else
123
124 TST_TEST_TCONF(NUMA_ERROR_MSG);
125
126 #endif /* HAVE_NUMA_H */
127