• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 0;
131 	long mem_used = 0;
132 
133 	/* Make sure there is some space for kernel upkeeping as well */
134 	min_kb += 4096;
135 
136 	snprintf(path, sizeof(path), "/sys/devices/system/node/node%i/meminfo", node);
137 
138 	if (access(path, F_OK)) {
139 		tst_res(TINFO, "File '%s' does not exist! NUMA not enabled?", path);
140 		return 0;
141 	}
142 
143 	FILE *fp = fopen(path, "r");
144 	if (!fp)
145 		tst_brk(TBROK | TERRNO, "Failed to open '%s'", path);
146 
147 	while (fgets(buf, sizeof(buf), fp)) {
148 		long val;
149 
150 		if (sscanf(buf, "%*s %*i MemTotal: %li", &val) == 1)
151 			mem_total = val;
152 
153 		if (sscanf(buf, "%*s %*i MemUsed: %li", &val) == 1)
154 			mem_used = val;
155 	}
156 
157 	fclose(fp);
158 
159 	if (!mem_total || !mem_used) {
160 		tst_res(TWARN, "Failed to parse '%s'", path);
161 		return 0;
162 	}
163 
164 	if (mem_total - mem_used < (long)min_kb) {
165 		tst_res(TINFO,
166 		        "Not enough free RAM on node %i, have %likB needs %zukB",
167 		        node, mem_total - mem_used, min_kb);
168 		return 0;
169 	}
170 
171 	return 1;
172 }
173 
tst_get_nodemap(int type,size_t min_mem_kb)174 struct tst_nodemap *tst_get_nodemap(int type, size_t min_mem_kb)
175 {
176 	struct bitmask *membind;
177 	struct tst_nodemap *nodes;
178 	unsigned int i, cnt;
179 
180 	if (type & ~(TST_NUMA_MEM))
181 		tst_brk(TBROK, "Invalid type %i\n", type);
182 
183 	membind = numa_get_membind();
184 
185 	cnt = 0;
186 	for (i = 0; i < membind->size; i++) {
187 		if (type & TST_NUMA_MEM && !numa_bitmask_isbitset(membind, i))
188 			continue;
189 
190 		cnt++;
191 	}
192 
193 	tst_res(TINFO, "Found %u NUMA memory nodes", cnt);
194 
195 	nodes = SAFE_MALLOC(sizeof(struct tst_nodemap)
196 	                    + sizeof(unsigned int) * cnt);
197 	nodes->cnt = cnt;
198 	nodes->counters = NULL;
199 
200 	cnt = 0;
201 	for (i = 0; i < membind->size; i++) {
202 		if (type & TST_NUMA_MEM &&
203 		    (!numa_bitmask_isbitset(membind, i) ||
204 		     !node_has_enough_memory(i, min_mem_kb)))
205 			continue;
206 
207 		nodes->map[cnt++] = i;
208 	}
209 
210 	nodes->cnt = cnt;
211 
212 	numa_bitmask_free(membind);
213 
214 	return nodes;
215 }
216 
217 #endif
218