1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2018 Cyril Hrubis <chrubis@suse.cz>
4 */
5
6/*
7 * We are testing set_mempolicy() with MPOL_BIND and MPOL_PREFERRED.
8 *
9 * For each node with memory we set its bit in nodemask with set_mempolicy()
10 * and verify that memory has been allocated accordingly.
11 */
12
13#include <errno.h>
14#include "config.h"
15#ifdef HAVE_NUMA_V2
16# include <numa.h>
17# include <numaif.h>
18#endif
19#include "tst_test.h"
20#include "tst_numa.h"
21
22#ifdef HAVE_NUMA_V2
23
24#include "set_mempolicy.h"
25
26static size_t page_size;
27static struct tst_nodemap *nodes;
28
29#define PAGES_ALLOCATED 16u
30
31static void setup(void)
32{
33	page_size = getpagesize();
34
35	nodes = tst_get_nodemap(TST_NUMA_MEM, 2 * PAGES_ALLOCATED * page_size / 1024);
36	if (nodes->cnt <= 1)
37		tst_brk(TCONF, "Test requires at least two NUMA memory nodes");
38
39	/*
40	 * In most cases, set_mempolicy01 finish quickly, but when the platform
41	 * has multiple NUMA nodes, the test matrix combination grows exponentially
42	 * and bring about test time to increase extremely fast.
43	 *
44	 * Here reset the maximum runtime according to the NUMA nodes.
45	 */
46	tst_set_max_runtime(test.max_runtime * (1 << nodes->cnt/16));
47}
48
49static void cleanup(void)
50{
51	tst_nodemap_free(nodes);
52}
53
54static void verify_mempolicy(unsigned int node, int mode)
55{
56	struct bitmask *bm = numa_allocate_nodemask();
57	unsigned int i;
58
59	numa_bitmask_setbit(bm, node);
60
61	TEST(set_mempolicy(mode, bm->maskp, bm->size+1));
62
63	if (TST_RET) {
64		tst_res(TFAIL | TTERRNO,
65		        "set_mempolicy(%s) node %u",
66		        tst_mempolicy_mode_name(mode), node);
67		return;
68	}
69
70	tst_res(TPASS, "set_mempolicy(%s) node %u",
71	        tst_mempolicy_mode_name(mode), node);
72
73	numa_free_nodemask(bm);
74
75	const char *prefix = "child: ";
76
77	if (SAFE_FORK()) {
78		prefix = "parent: ";
79		tst_reap_children();
80	}
81
82	tst_nodemap_reset_counters(nodes);
83	alloc_fault_count(nodes, NULL, PAGES_ALLOCATED * page_size);
84	tst_nodemap_print_counters(nodes);
85
86	for (i = 0; i < nodes->cnt; i++) {
87		if (nodes->map[i] == node) {
88			if (nodes->counters[i] == PAGES_ALLOCATED) {
89				tst_res(TPASS, "%sNode %u allocated %u",
90				        prefix, node, PAGES_ALLOCATED);
91			} else {
92				tst_res(TFAIL, "%sNode %u allocated %u, expected %u",
93				        prefix, node, nodes->counters[i],
94				        PAGES_ALLOCATED);
95			}
96			continue;
97		}
98
99		if (nodes->counters[i]) {
100			tst_res(TFAIL, "%sNode %u allocated %u, expected 0",
101			        prefix, i, nodes->counters[i]);
102		}
103	}
104}
105
106static void verify_set_mempolicy(unsigned int n)
107{
108	unsigned int i;
109	int mode = n ? MPOL_PREFERRED : MPOL_BIND;
110
111	for (i = 0; i < nodes->cnt; i++)
112		verify_mempolicy(nodes->map[i], mode);
113}
114
115static struct tst_test test = {
116	.setup = setup,
117	.cleanup = cleanup,
118	.test = verify_set_mempolicy,
119	.tcnt = 2,
120	.forks_child = 1,
121	.needs_checkpoints = 1,
122	.max_runtime = 600,
123};
124
125#else
126
127TST_TEST_TCONF(NUMA_ERROR_MSG);
128
129#endif /* HAVE_NUMA_V2 */
130