1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2019 Cyril Hrubis <chrubis@suse.cz>
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
25static size_t page_size;
26static struct tst_nodemap *nodes;
27
28static 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
37static void cleanup(void)
38{
39	tst_nodemap_free(nodes);
40}
41
42static 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
97exit:
98	tst_numa_unmap(ptr, size);
99	numa_free_nodemask(bm);
100}
101
102static const int modes[] = {
103	MPOL_PREFERRED,
104	MPOL_BIND,
105	MPOL_INTERLEAVE,
106};
107
108static 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
114static 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
124TST_TEST_TCONF(NUMA_ERROR_MSG);
125
126#endif /* HAVE_NUMA_H */
127