1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 Cyril Hrubis <chrubis@suse.cz>
4 */
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <ctype.h>
9 #include "config.h"
10 #ifdef HAVE_NUMA_V2
11 # include <numa.h>
12 # include <numaif.h>
13 #endif
14
15 #define TST_NO_DEFAULT_MAIN
16 #include "tst_test.h"
17 #include "tst_numa.h"
18 #include "lapi/numaif.h"
19
tst_nodemap_print_counters(struct tst_nodemap * nodes)20 void tst_nodemap_print_counters(struct tst_nodemap *nodes)
21 {
22 unsigned int i;
23
24 for (i = 0; i < nodes->cnt; i++) {
25 tst_res(TINFO, "Node %i allocated %u pages",
26 nodes->map[i], nodes->counters[i]);
27 }
28 }
29
tst_nodemap_reset_counters(struct tst_nodemap * nodes)30 void tst_nodemap_reset_counters(struct tst_nodemap *nodes)
31 {
32 size_t arr_size = sizeof(unsigned int) * nodes->cnt;
33
34 if (!nodes->counters)
35 nodes->counters = SAFE_MALLOC(arr_size);
36
37 memset(nodes->counters, 0, arr_size);
38 }
39
tst_nodemap_free(struct tst_nodemap * nodes)40 void tst_nodemap_free(struct tst_nodemap *nodes)
41 {
42 free(nodes->counters);
43 free(nodes);
44 }
45
46 #ifdef HAVE_NUMA_V2
47
tst_mempolicy_mode_name(int mode)48 const char *tst_mempolicy_mode_name(int mode)
49 {
50 switch (mode) {
51 case MPOL_DEFAULT:
52 return "MPOL_DEFAULT";
53 case MPOL_PREFERRED:
54 return "MPOL_PREFERRED";
55 case MPOL_BIND:
56 return "MPOL_BIND";
57 case MPOL_INTERLEAVE:
58 return "MPOL_INTERLEAVE";
59 case MPOL_LOCAL:
60 return "MPOL_LOCAL";
61 default:
62 return "???";
63 }
64 }
65
66
inc_counter(unsigned int node,struct tst_nodemap * nodes)67 static void inc_counter(unsigned int node, struct tst_nodemap *nodes)
68 {
69 unsigned int i;
70
71 for (i = 0; i < nodes->cnt; i++) {
72 if (nodes->map[i] == node) {
73 nodes->counters[i]++;
74 break;
75 }
76 }
77 }
78
tst_nodemap_count_pages(struct tst_nodemap * nodes,void * ptr,size_t size)79 void tst_nodemap_count_pages(struct tst_nodemap *nodes,
80 void *ptr, size_t size)
81 {
82 size_t page_size = getpagesize();
83 unsigned int i;
84 int node;
85 long ret;
86 unsigned int pages = (size + page_size - 1)/page_size;
87
88 for (i = 0; i < pages; i++) {
89 ret = get_mempolicy(&node, NULL, 0, ptr + i * page_size, MPOL_F_NODE | MPOL_F_ADDR);
90 if (ret < 0)
91 tst_brk(TBROK | TERRNO, "get_mempolicy() failed");
92
93 if (node < 0) {
94 tst_res(TWARN,
95 "get_mempolicy(...) returned invalid node %i\n", node);
96 continue;
97 }
98
99 inc_counter(node, nodes);
100 }
101 }
102
tst_numa_map(const char * path,size_t size)103 void *tst_numa_map(const char *path, size_t size)
104 {
105 char *ptr;
106 int fd = -1;
107 int flags = MAP_PRIVATE|MAP_ANONYMOUS;
108
109 if (path) {
110 fd = SAFE_OPEN(path, O_CREAT | O_EXCL | O_RDWR, 0666);
111 SAFE_FTRUNCATE(fd, size);
112 flags = MAP_SHARED;
113 }
114
115 ptr = SAFE_MMAP(NULL, size,
116 PROT_READ|PROT_WRITE, flags, fd, 0);
117
118 if (path) {
119 SAFE_CLOSE(fd);
120 SAFE_UNLINK(path);
121 }
122
123 return ptr;
124 }
125
node_has_enough_memory(int node,size_t min_kb)126 static int node_has_enough_memory(int node, size_t min_kb)
127 {
128 char path[1024];
129 char buf[1024];
130 long mem_total = -1;
131 long mem_used = -1;
132 long file_pages = 0;
133 long mem_avail;
134
135 /* Make sure there is some space for kernel upkeeping as well */
136 min_kb += 4096;
137
138 snprintf(path, sizeof(path), "/sys/devices/system/node/node%i/meminfo", node);
139
140 if (access(path, F_OK)) {
141 tst_res(TINFO, "File '%s' does not exist! NUMA not enabled?", path);
142 return 0;
143 }
144
145 FILE *fp = fopen(path, "r");
146 if (!fp)
147 tst_brk(TBROK | TERRNO, "Failed to open '%s'", path);
148
149 while (fgets(buf, sizeof(buf), fp)) {
150 long val;
151
152 if (sscanf(buf, "%*s %*i MemTotal: %li", &val) == 1)
153 mem_total = val;
154
155 if (sscanf(buf, "%*s %*i MemUsed: %li", &val) == 1)
156 mem_used = val;
157
158 if (sscanf(buf, "%*s %*i FilePages: %li", &val) == 1)
159 file_pages = val;
160 }
161
162 fclose(fp);
163
164 if (mem_total == -1 || mem_used == -1) {
165 tst_res(TWARN, "Failed to parse '%s'", path);
166 return 0;
167 }
168
169 mem_avail = mem_total - mem_used + (9 * file_pages)/10;
170
171 if (mem_avail < (long)min_kb) {
172 tst_res(TINFO,
173 "Not enough free RAM on node %i, have %likB needs %zukB",
174 node, mem_avail, min_kb);
175 return 0;
176 }
177
178 return 1;
179 }
180
tst_get_nodemap(int type,size_t min_mem_kb)181 struct tst_nodemap *tst_get_nodemap(int type, size_t min_mem_kb)
182 {
183 struct bitmask *membind;
184 struct tst_nodemap *nodes;
185 unsigned int i, cnt;
186
187 if (type & ~(TST_NUMA_MEM))
188 tst_brk(TBROK, "Invalid type %i\n", type);
189
190 membind = numa_get_membind();
191
192 cnt = 0;
193 for (i = 0; i < membind->size; i++) {
194 if (type & TST_NUMA_MEM && !numa_bitmask_isbitset(membind, i))
195 continue;
196
197 cnt++;
198 }
199
200 tst_res(TINFO, "Found %u NUMA memory nodes", cnt);
201
202 nodes = SAFE_MALLOC(sizeof(struct tst_nodemap)
203 + sizeof(unsigned int) * cnt);
204 nodes->cnt = cnt;
205 nodes->counters = NULL;
206
207 cnt = 0;
208 for (i = 0; i < membind->size; i++) {
209 if (type & TST_NUMA_MEM &&
210 (!numa_bitmask_isbitset(membind, i) ||
211 !node_has_enough_memory(i, min_mem_kb)))
212 continue;
213
214 nodes->map[cnt++] = i;
215 }
216
217 nodes->cnt = cnt;
218
219 numa_bitmask_free(membind);
220
221 return nodes;
222 }
223
224 #endif
225