• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #define TST_NO_DEFAULT_MAIN
2 
3 #include "config.h"
4 #include <sys/types.h>
5 #include <sys/mman.h>
6 #include <sys/mount.h>
7 #include <sys/stat.h>
8 #include <sys/wait.h>
9 #include <sys/param.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #if HAVE_NUMA_H
13 #include <numa.h>
14 #endif
15 #if HAVE_NUMAIF_H
16 #include <numaif.h>
17 #endif
18 #include <pthread.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 
25 #include "mem.h"
26 #include "numa_helper.h"
27 
28 /* OOM */
29 
30 long overcommit = -1;
31 
alloc_mem(long int length,int testcase)32 static int alloc_mem(long int length, int testcase)
33 {
34 	char *s;
35 	long i, pagesz = getpagesize();
36 	int loop = 10;
37 
38 	tst_res(TINFO, "thread (%lx), allocating %ld bytes.",
39 		(unsigned long) pthread_self(), length);
40 
41 	s = mmap(NULL, length, PROT_READ | PROT_WRITE,
42 		 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
43 	if (s == MAP_FAILED)
44 		return errno;
45 
46 	if (testcase == MLOCK) {
47 		while (mlock(s, length) == -1 && loop > 0) {
48 			if (EAGAIN != errno)
49 				return errno;
50 			usleep(300000);
51 			loop--;
52 		}
53 	}
54 
55 #ifdef HAVE_DECL_MADV_MERGEABLE
56 	if (testcase == KSM && madvise(s, length, MADV_MERGEABLE) == -1)
57 		return errno;
58 #endif
59 	for (i = 0; i < length; i += pagesz)
60 		s[i] = '\a';
61 
62 	return 0;
63 }
64 
child_alloc_thread(void * args)65 static void *child_alloc_thread(void *args)
66 {
67 	int ret = 0;
68 
69 	/* keep allocating until there's an error */
70 	while (!ret)
71 		ret = alloc_mem(LENGTH, (long)args);
72 	exit(ret);
73 }
74 
child_alloc(int testcase,int lite,int threads)75 static void child_alloc(int testcase, int lite, int threads)
76 {
77 	int i;
78 	pthread_t *th;
79 
80 	if (lite) {
81 		int ret = alloc_mem(TESTMEM * 2 + MB, testcase);
82 		exit(ret);
83 	}
84 
85 	th = malloc(sizeof(pthread_t) * threads);
86 	if (!th) {
87 		tst_res(TINFO | TERRNO, "malloc");
88 		goto out;
89 	}
90 
91 	for (i = 0; i < threads; i++) {
92 		TEST(pthread_create(&th[i], NULL, child_alloc_thread,
93 			(void *)((long)testcase)));
94 		if (TST_RET) {
95 			tst_res(TINFO | TRERRNO, "pthread_create");
96 			/*
97 			 * Keep going if thread other than first fails to
98 			 * spawn due to lack of resources.
99 			 */
100 			if (i == 0 || TST_RET != EAGAIN)
101 				goto out;
102 		}
103 	}
104 
105 	/* wait for one of threads to exit whole process */
106 	while (1)
107 		sleep(1);
108 out:
109 	exit(1);
110 }
111 
112 /*
113  * oom - allocates memory according to specified testcase and checks
114  *       desired outcome (e.g. child killed, operation failed with ENOMEM)
115  * @testcase: selects how child allocates memory
116  *            valid choices are: NORMAL, MLOCK and KSM
117  * @lite: if non-zero, child makes only single TESTMEM+MB allocation
118  *        if zero, child keeps allocating memory until it gets killed
119  *        or some operation fails
120  * @retcode: expected return code of child process
121  *           if matches child ret code, this function reports PASS,
122  *           otherwise it reports FAIL
123  * @allow_sigkill: if zero and child is killed, this function reports FAIL
124  *                 if non-zero, then if child is killed by SIGKILL
125  *                 it is considered as PASS
126  */
oom(int testcase,int lite,int retcode,int allow_sigkill)127 void oom(int testcase, int lite, int retcode, int allow_sigkill)
128 {
129 	pid_t pid;
130 	int status, threads;
131 
132 	tst_enable_oom_protection(0);
133 
134 	switch (pid = SAFE_FORK()) {
135 	case 0:
136 		tst_disable_oom_protection(0);
137 		threads = MAX(1, tst_ncpus() - 1);
138 		child_alloc(testcase, lite, threads);
139 	default:
140 		break;
141 	}
142 
143 	tst_res(TINFO, "expected victim is %d.", pid);
144 	SAFE_WAITPID(-1, &status, 0);
145 
146 	if (WIFSIGNALED(status)) {
147 		if (allow_sigkill && WTERMSIG(status) == SIGKILL) {
148 			tst_res(TPASS, "victim signalled: (%d) %s",
149 				SIGKILL,
150 				tst_strsig(SIGKILL));
151 		} else {
152 			tst_res(TFAIL, "victim signalled: (%d) %s",
153 				WTERMSIG(status),
154 				tst_strsig(WTERMSIG(status)));
155 		}
156 	} else if (WIFEXITED(status)) {
157 		if (WEXITSTATUS(status) == retcode) {
158 			tst_res(TPASS, "victim retcode: (%d) %s",
159 				retcode, strerror(retcode));
160 		} else {
161 			tst_res(TFAIL, "victim unexpectedly ended with "
162 				"retcode: %d, expected: %d",
163 				WEXITSTATUS(status), retcode);
164 		}
165 	} else {
166 		tst_res(TFAIL, "victim unexpectedly ended");
167 	}
168 }
169 
170 #ifdef HAVE_NUMA_V2
set_global_mempolicy(int mempolicy)171 static void set_global_mempolicy(int mempolicy)
172 {
173 	unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 };
174 	int num_nodes, *nodes;
175 	int ret;
176 
177 	if (mempolicy) {
178 		ret = get_allowed_nodes_arr(NH_MEMS|NH_CPUS, &num_nodes, &nodes);
179 		if (ret != 0)
180 			tst_brk(TBROK|TERRNO, "get_allowed_nodes_arr");
181 		if (num_nodes < 2) {
182 			tst_res(TINFO, "mempolicy need NUMA system support");
183 			free(nodes);
184 			return;
185 		}
186 		switch(mempolicy) {
187 		case MPOL_BIND:
188 			/* bind the second node */
189 			set_node(nmask, nodes[1]);
190 			break;
191 		case MPOL_INTERLEAVE:
192 		case MPOL_PREFERRED:
193 			if (num_nodes == 2) {
194 				tst_res(TINFO, "The mempolicy need "
195 					 "more than 2 numa nodes");
196 				free(nodes);
197 				return;
198 			} else {
199 				/* Using the 2nd,3rd node */
200 				set_node(nmask, nodes[1]);
201 				set_node(nmask, nodes[2]);
202 			}
203 			break;
204 		default:
205 			tst_brk(TBROK|TERRNO, "Bad mempolicy mode");
206 		}
207 		if (set_mempolicy(mempolicy, nmask, MAXNODES) == -1)
208 			tst_brk(TBROK|TERRNO, "set_mempolicy");
209 	}
210 }
211 #else
set_global_mempolicy(int mempolicy LTP_ATTRIBUTE_UNUSED)212 static void set_global_mempolicy(int mempolicy LTP_ATTRIBUTE_UNUSED) { }
213 #endif
214 
testoom(int mempolicy,int lite,int retcode,int allow_sigkill)215 void testoom(int mempolicy, int lite, int retcode, int allow_sigkill)
216 {
217 	int ksm_run_orig;
218 
219 	set_global_mempolicy(mempolicy);
220 
221 	tst_res(TINFO, "start normal OOM testing.");
222 	oom(NORMAL, lite, retcode, allow_sigkill);
223 
224 	tst_res(TINFO, "start OOM testing for mlocked pages.");
225 	oom(MLOCK, lite, retcode, allow_sigkill);
226 
227 	/*
228 	 * Skip oom(KSM) if lite == 1, since limit_in_bytes may vary from
229 	 * run to run, which isn't reliable for oom03 cgroup test.
230 	 */
231 	if (access(PATH_KSM, F_OK) == -1 || lite == 1) {
232 		tst_res(TINFO, "KSM is not configed or lite == 1, "
233 			 "skip OOM test for KSM pags");
234 	} else {
235 		tst_res(TINFO, "start OOM testing for KSM pages.");
236 		SAFE_FILE_SCANF(PATH_KSM "run", "%d", &ksm_run_orig);
237 		SAFE_FILE_PRINTF(PATH_KSM "run", "1");
238 		oom(KSM, lite, retcode, allow_sigkill);
239 		SAFE_FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig);
240 	}
241 }
242 
243 /* KSM */
244 
check(char * path,long int value)245 static void check(char *path, long int value)
246 {
247 	char fullpath[BUFSIZ];
248 	long actual_val;
249 
250 	snprintf(fullpath, BUFSIZ, PATH_KSM "%s", path);
251 	SAFE_FILE_SCANF(fullpath, "%ld", &actual_val);
252 
253 	if (actual_val != value)
254 		tst_res(TFAIL, "%s is not %ld but %ld.", path, value,
255 			actual_val);
256 	else
257 		tst_res(TPASS, "%s is %ld.", path, actual_val);
258 }
259 
final_group_check(int run,int pages_shared,int pages_sharing,int pages_volatile,int pages_unshared,int sleep_millisecs,int pages_to_scan)260 static void final_group_check(int run, int pages_shared, int pages_sharing,
261 			  int pages_volatile, int pages_unshared,
262 			  int sleep_millisecs, int pages_to_scan)
263 {
264 	tst_res(TINFO, "check!");
265 	check("run", run);
266 	check("pages_shared", pages_shared);
267 	check("pages_sharing", pages_sharing);
268 	check("pages_volatile", pages_volatile);
269 	check("pages_unshared", pages_unshared);
270 	check("sleep_millisecs", sleep_millisecs);
271 	check("pages_to_scan", pages_to_scan);
272 }
273 
group_check(int run,int pages_shared,int pages_sharing,int pages_volatile,int pages_unshared,int sleep_millisecs,int pages_to_scan)274 static void group_check(int run, int pages_shared, int pages_sharing,
275 			int pages_volatile, int pages_unshared,
276 			int sleep_millisecs, int pages_to_scan)
277 {
278 	if (run != 1) {
279 		tst_res(TFAIL, "group_check run is not 1, %d.", run);
280 	} else {
281 		/* wait for ksm daemon to scan all mergeable pages. */
282 		wait_ksmd_full_scan();
283 	}
284 
285 	final_group_check(run, pages_shared, pages_sharing,
286 			  pages_volatile, pages_unshared,
287 			  sleep_millisecs, pages_to_scan);
288 }
289 
verify(char ** memory,char value,int proc,int start,int end,int start2,int end2)290 static void verify(char **memory, char value, int proc,
291 		    int start, int end, int start2, int end2)
292 {
293 	int i, j;
294 	void *s = NULL;
295 
296 	s = SAFE_MALLOC((end - start) * (end2 - start2));
297 
298 	tst_res(TINFO, "child %d verifies memory content.", proc);
299 	memset(s, value, (end - start) * (end2 - start2));
300 	if (memcmp(memory[start], s, (end - start) * (end2 - start2))
301 	    != 0)
302 		for (j = start; j < end; j++)
303 			for (i = start2; i < end2; i++)
304 				if (memory[j][i] != value)
305 					tst_res(TFAIL, "child %d has %c at "
306 						 "%d,%d,%d.",
307 						 proc, memory[j][i], proc,
308 						 j, i);
309 	free(s);
310 }
311 
check_hugepage(void)312 void check_hugepage(void)
313 {
314 	if (access(PATH_HUGEPAGES, F_OK))
315 		tst_brk(TCONF, "Huge page is not supported.");
316 }
317 
318 struct ksm_merge_data {
319 	char data;
320 	unsigned int mergeable_size;
321 };
322 
ksm_child_memset(int child_num,int size,int total_unit,struct ksm_merge_data ksm_merge_data,char ** memory)323 static void ksm_child_memset(int child_num, int size, int total_unit,
324 		 struct ksm_merge_data ksm_merge_data, char **memory)
325 {
326 	int i = 0, j;
327 	int unit = size / total_unit;
328 
329 	tst_res(TINFO, "child %d continues...", child_num);
330 
331 	if (ksm_merge_data.mergeable_size == size * MB) {
332 		tst_res(TINFO, "child %d allocates %d MB filled with '%c'",
333 			child_num, size, ksm_merge_data.data);
334 
335 	} else {
336 		tst_res(TINFO, "child %d allocates %d MB filled with '%c'"
337 				" except one page with 'e'",
338 				child_num, size, ksm_merge_data.data);
339 	}
340 
341 	for (j = 0; j < total_unit; j++) {
342 		for (i = 0; (unsigned int)i < unit * MB; i++)
343 			memory[j][i] = ksm_merge_data.data;
344 	}
345 
346 	/* if it contains unshared page, then set 'e' char
347 	 * at the end of the last page
348 	 */
349 	if (ksm_merge_data.mergeable_size < size * MB)
350 		memory[j-1][i-1] = 'e';
351 }
352 
create_ksm_child(int child_num,int size,int unit,struct ksm_merge_data * ksm_merge_data)353 static void create_ksm_child(int child_num, int size, int unit,
354 		       struct ksm_merge_data *ksm_merge_data)
355 {
356 	int j, total_unit;
357 	char **memory;
358 
359 	/* The total units in all */
360 	total_unit = size / unit;
361 
362 	/* Apply for the space for memory */
363 	memory = SAFE_MALLOC(total_unit * sizeof(char *));
364 	for (j = 0; j < total_unit; j++) {
365 		memory[j] = SAFE_MMAP(NULL, unit * MB, PROT_READ|PROT_WRITE,
366 			MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
367 #ifdef HAVE_DECL_MADV_MERGEABLE
368 		if (madvise(memory[j], unit * MB, MADV_MERGEABLE) == -1)
369 			tst_brk(TBROK|TERRNO, "madvise");
370 #endif
371 	}
372 
373 	tst_res(TINFO, "child %d stops.", child_num);
374 	if (raise(SIGSTOP) == -1)
375 		tst_brk(TBROK|TERRNO, "kill");
376 	fflush(stdout);
377 
378 	for (j = 0; j < 4; j++) {
379 
380 		ksm_child_memset(child_num, size, total_unit,
381 				  ksm_merge_data[j], memory);
382 
383 		fflush(stdout);
384 
385 		tst_res(TINFO, "child %d stops.", child_num);
386 		if (raise(SIGSTOP) == -1)
387 			tst_brk(TBROK|TERRNO, "kill");
388 
389 		if (ksm_merge_data[j].mergeable_size < size * MB) {
390 			verify(memory, 'e', child_num, total_unit - 1,
391 				total_unit, unit * MB - 1, unit * MB);
392 			verify(memory, ksm_merge_data[j].data, child_num,
393 				0, total_unit, 0, unit * MB - 1);
394 		} else {
395 			verify(memory, ksm_merge_data[j].data, child_num,
396 				0, total_unit, 0, unit * MB);
397 		}
398 	}
399 
400 	tst_res(TINFO, "child %d finished.", child_num);
401 }
402 
stop_ksm_children(int * child,int num)403 static void stop_ksm_children(int *child, int num)
404 {
405 	int k, status;
406 
407 	tst_res(TINFO, "wait for all children to stop.");
408 	for (k = 0; k < num; k++) {
409 		SAFE_WAITPID(child[k], &status, WUNTRACED);
410 		if (!WIFSTOPPED(status))
411 			tst_brk(TBROK, "child %d was not stopped", k);
412 	}
413 }
414 
resume_ksm_children(int * child,int num)415 static void resume_ksm_children(int *child, int num)
416 {
417 	int k;
418 
419 	tst_res(TINFO, "resume all children.");
420 	for (k = 0; k < num; k++)
421 		SAFE_KILL(child[k], SIGCONT);
422 
423 	fflush(stdout);
424 }
425 
create_same_memory(int size,int num,int unit)426 void create_same_memory(int size, int num, int unit)
427 {
428 	int i, j, status, *child;
429 	unsigned long ps, pages;
430 	struct ksm_merge_data **ksm_data;
431 
432 	struct ksm_merge_data ksm_data0[] = {
433 	       {'c', size*MB}, {'c', size*MB}, {'d', size*MB}, {'d', size*MB},
434 	};
435 	struct ksm_merge_data ksm_data1[] = {
436 	       {'a', size*MB}, {'b', size*MB}, {'d', size*MB}, {'d', size*MB-1},
437 	};
438 	struct ksm_merge_data ksm_data2[] = {
439 	       {'a', size*MB}, {'a', size*MB}, {'d', size*MB}, {'d', size*MB},
440 	};
441 
442 	ps = sysconf(_SC_PAGE_SIZE);
443 	pages = MB / ps;
444 
445 	ksm_data = malloc((num - 3) * sizeof(struct ksm_merge_data *));
446 	/* Since from third child, the data is same with the first child's */
447 	for (i = 0; i < num - 3; i++) {
448 		ksm_data[i] = malloc(4 * sizeof(struct ksm_merge_data));
449 		for (j = 0; j < 4; j++) {
450 			ksm_data[i][j].data = ksm_data0[j].data;
451 			ksm_data[i][j].mergeable_size =
452 				ksm_data0[j].mergeable_size;
453 		}
454 	}
455 
456 	child = SAFE_MALLOC(num * sizeof(int));
457 
458 	for (i = 0; i < num; i++) {
459 		fflush(stdout);
460 		switch (child[i] = SAFE_FORK()) {
461 		case 0:
462 			if (i == 0) {
463 				create_ksm_child(i, size, unit, ksm_data0);
464 				exit(0);
465 			} else if (i == 1) {
466 				create_ksm_child(i, size, unit, ksm_data1);
467 				exit(0);
468 			} else if (i == 2) {
469 				create_ksm_child(i, size, unit, ksm_data2);
470 				exit(0);
471 			} else {
472 				create_ksm_child(i, size, unit, ksm_data[i-3]);
473 				exit(0);
474 			}
475 		}
476 	}
477 
478 	stop_ksm_children(child, num);
479 
480 	tst_res(TINFO, "KSM merging...");
481 	if (access(PATH_KSM "max_page_sharing", F_OK) == 0) {
482 		SAFE_FILE_PRINTF(PATH_KSM "run", "2");
483 		SAFE_FILE_PRINTF(PATH_KSM "max_page_sharing", "%ld", size * pages * num);
484 	}
485 
486 	SAFE_FILE_PRINTF(PATH_KSM "run", "1");
487 	SAFE_FILE_PRINTF(PATH_KSM "pages_to_scan", "%ld", size * pages * num);
488 	SAFE_FILE_PRINTF(PATH_KSM "sleep_millisecs", "0");
489 
490 	resume_ksm_children(child, num);
491 	stop_ksm_children(child, num);
492 	group_check(1, 2, size * num * pages - 2, 0, 0, 0, size * pages * num);
493 
494 	resume_ksm_children(child, num);
495 	stop_ksm_children(child, num);
496 	group_check(1, 3, size * num * pages - 3, 0, 0, 0, size * pages * num);
497 
498 	resume_ksm_children(child, num);
499 	stop_ksm_children(child, num);
500 	group_check(1, 1, size * num * pages - 1, 0, 0, 0, size * pages * num);
501 
502 	resume_ksm_children(child, num);
503 	stop_ksm_children(child, num);
504 	group_check(1, 1, size * num * pages - 2, 0, 1, 0, size * pages * num);
505 
506 	tst_res(TINFO, "KSM unmerging...");
507 	SAFE_FILE_PRINTF(PATH_KSM "run", "2");
508 
509 	resume_ksm_children(child, num);
510 	final_group_check(2, 0, 0, 0, 0, 0, size * pages * num);
511 
512 	tst_res(TINFO, "stop KSM.");
513 	SAFE_FILE_PRINTF(PATH_KSM "run", "0");
514 	final_group_check(0, 0, 0, 0, 0, 0, size * pages * num);
515 
516 	while (waitpid(-1, &status, 0) > 0)
517 		if (WEXITSTATUS(status) != 0)
518 			tst_res(TFAIL, "child exit status is %d",
519 				 WEXITSTATUS(status));
520 }
521 
test_ksm_merge_across_nodes(unsigned long nr_pages)522 void test_ksm_merge_across_nodes(unsigned long nr_pages)
523 {
524 	char **memory;
525 	int i, ret;
526 	int num_nodes, *nodes;
527 	unsigned long length;
528 	unsigned long pagesize;
529 
530 #ifdef HAVE_NUMA_V2
531 	unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 };
532 #endif
533 
534 	ret = get_allowed_nodes_arr(NH_MEMS, &num_nodes, &nodes);
535 	if (ret != 0)
536 		tst_brk(TBROK|TERRNO, "get_allowed_nodes_arr");
537 	if (num_nodes < 2) {
538 		tst_res(TINFO, "need NUMA system support");
539 		free(nodes);
540 		return;
541 	}
542 
543 	pagesize = sysconf(_SC_PAGE_SIZE);
544 	length = nr_pages * pagesize;
545 
546 	memory = SAFE_MALLOC(num_nodes * sizeof(char *));
547 	for (i = 0; i < num_nodes; i++) {
548 		memory[i] = SAFE_MMAP(NULL, length, PROT_READ|PROT_WRITE,
549 			    MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
550 #ifdef HAVE_DECL_MADV_MERGEABLE
551 		if (madvise(memory[i], length, MADV_MERGEABLE) == -1)
552 			tst_brk(TBROK|TERRNO, "madvise");
553 #endif
554 
555 #ifdef HAVE_NUMA_V2
556 		clean_node(nmask);
557 		set_node(nmask, nodes[i]);
558 		/*
559 		 * Use mbind() to make sure each node contains
560 		 * length size memory.
561 		 */
562 		ret = mbind(memory[i], length, MPOL_BIND, nmask, MAXNODES, 0);
563 		if (ret == -1)
564 			tst_brk(TBROK|TERRNO, "mbind");
565 #endif
566 
567 		memset(memory[i], 10, length);
568 	}
569 
570 	SAFE_FILE_PRINTF(PATH_KSM "sleep_millisecs", "0");
571 	SAFE_FILE_PRINTF(PATH_KSM "pages_to_scan", "%ld",
572 			 nr_pages * num_nodes);
573 	/*
574 	 * merge_across_nodes and max_page_sharing setting can be changed
575 	 * only when there are no ksm shared pages in system, so set run 2
576 	 * to unmerge pages first, then to 1 after changing merge_across_nodes,
577 	 * to remerge according to the new setting.
578 	 */
579 	SAFE_FILE_PRINTF(PATH_KSM "run", "2");
580 	if (access(PATH_KSM "max_page_sharing", F_OK) == 0)
581 		SAFE_FILE_PRINTF(PATH_KSM "max_page_sharing",
582 			"%ld", nr_pages * num_nodes);
583 	tst_res(TINFO, "Start to test KSM with merge_across_nodes=1");
584 	SAFE_FILE_PRINTF(PATH_KSM "merge_across_nodes", "1");
585 	SAFE_FILE_PRINTF(PATH_KSM "run", "1");
586 	group_check(1, 1, nr_pages * num_nodes - 1, 0, 0, 0,
587 		    nr_pages * num_nodes);
588 
589 	SAFE_FILE_PRINTF(PATH_KSM "run", "2");
590 	tst_res(TINFO, "Start to test KSM with merge_across_nodes=0");
591 	SAFE_FILE_PRINTF(PATH_KSM "merge_across_nodes", "0");
592 	SAFE_FILE_PRINTF(PATH_KSM "run", "1");
593 	group_check(1, num_nodes, nr_pages * num_nodes - num_nodes,
594 		    0, 0, 0, nr_pages * num_nodes);
595 
596 	SAFE_FILE_PRINTF(PATH_KSM "run", "2");
597 }
598 
599 /* THP */
600 
601 /* cpuset/memcg */
gather_node_cpus(char * cpus,long nd)602 static void gather_node_cpus(char *cpus, long nd)
603 {
604 	int ncpus = 0;
605 	int i;
606 	long online;
607 	char buf[BUFSIZ];
608 	char path[BUFSIZ], path1[BUFSIZ];
609 
610 	while (path_exist(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus))
611 		ncpus++;
612 
613 	for (i = 0; i < ncpus; i++) {
614 		snprintf(path, BUFSIZ,
615 			 PATH_SYS_SYSTEM "/node/node%ld/cpu%d", nd, i);
616 		if (path_exist(path)) {
617 			snprintf(path1, BUFSIZ, "%s/online", path);
618 			/*
619 			 * if there is no online knob, then the cpu cannot
620 			 * be taken offline
621 			 */
622 			if (path_exist(path1)) {
623 				SAFE_FILE_SCANF(path1, "%ld", &online);
624 				if (online == 0)
625 					continue;
626 			}
627 			sprintf(buf, "%d,", i);
628 			strcat(cpus, buf);
629 		}
630 	}
631 	/* Remove the trailing comma. */
632 	cpus[strlen(cpus) - 1] = '\0';
633 }
634 
write_cpusets(const struct tst_cgroup_group * cg,long nd)635 void write_cpusets(const struct tst_cgroup_group *cg, long nd)
636 {
637 	char cpus[BUFSIZ] = "";
638 
639 	SAFE_CGROUP_PRINTF(cg, "cpuset.mems", "%ld", nd);
640 
641 	gather_node_cpus(cpus, nd);
642 	/*
643 	 * If the 'nd' node doesn't contain any CPUs,
644 	 * the first ID of CPU '0' will be used as
645 	 * the value of cpuset.cpus.
646 	 */
647 	if (strlen(cpus) != 0) {
648 		SAFE_CGROUP_PRINT(cg, "cpuset.cpus", cpus);
649 	} else {
650 		tst_res(TINFO, "No CPUs in the node%ld; "
651 				"using only CPU0", nd);
652 		SAFE_CGROUP_PRINT(cg, "cpuset.cpus", "0");
653 	}
654 }
655 
656 /* shared */
657 
658 /* Warning: *DO NOT* use this function in child */
get_a_numa_node(void)659 unsigned int get_a_numa_node(void)
660 {
661 	unsigned int nd1, nd2;
662 	int ret;
663 
664 	ret = get_allowed_nodes(0, 2, &nd1, &nd2);
665 	switch (ret) {
666 	case 0:
667 		break;
668 	case -3:
669 		tst_brk(TCONF, "requires a NUMA system.");
670 	default:
671 		tst_brk(TBROK | TERRNO, "1st get_allowed_nodes");
672 	}
673 
674 	ret = get_allowed_nodes(NH_MEMS | NH_CPUS, 1, &nd1);
675 	switch (ret) {
676 	case 0:
677 		tst_res(TINFO, "get node%u.", nd1);
678 		return nd1;
679 	case -3:
680 		tst_brk(TCONF, "requires a NUMA system that has "
681 			 "at least one node with both memory and CPU "
682 			 "available.");
683 	default:
684 		tst_brk(TBROK | TERRNO, "2nd get_allowed_nodes");
685 	}
686 
687 	/* not reached */
688 	abort();
689 }
690 
path_exist(const char * path,...)691 int path_exist(const char *path, ...)
692 {
693 	va_list ap;
694 	char pathbuf[PATH_MAX];
695 
696 	va_start(ap, path);
697 	vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
698 	va_end(ap);
699 
700 	return access(pathbuf, F_OK) == 0;
701 }
702 
set_sys_tune(char * sys_file,long tune,int check)703 void set_sys_tune(char *sys_file, long tune, int check)
704 {
705 	long val;
706 	char path[BUFSIZ];
707 
708 	tst_res(TINFO, "set %s to %ld", sys_file, tune);
709 
710 	snprintf(path, BUFSIZ, PATH_SYSVM "%s", sys_file);
711 	SAFE_FILE_PRINTF(path, "%ld", tune);
712 
713 	if (check) {
714 		val = get_sys_tune(sys_file);
715 		if (val != tune)
716 			tst_brk(TBROK, "%s = %ld, but expect %ld",
717 				 sys_file, val, tune);
718 	}
719 }
720 
get_sys_tune(char * sys_file)721 long get_sys_tune(char *sys_file)
722 {
723 	char path[BUFSIZ];
724 	long tune;
725 
726 	snprintf(path, BUFSIZ, PATH_SYSVM "%s", sys_file);
727 	SAFE_FILE_SCANF(path, "%ld", &tune);
728 
729 	return tune;
730 }
731 
update_shm_size(size_t * shm_size)732 void update_shm_size(size_t * shm_size)
733 {
734 	size_t shmmax;
735 
736 	SAFE_FILE_SCANF(PATH_SHMMAX, "%zu", &shmmax);
737 	if (*shm_size > shmmax) {
738 		tst_res(TINFO, "Set shm_size to shmmax: %zu", shmmax);
739 		*shm_size = shmmax;
740 	}
741 }
742 
range_is_mapped(unsigned long low,unsigned long high)743 int range_is_mapped(unsigned long low, unsigned long high)
744 {
745 	FILE *fp;
746 
747 	fp = fopen("/proc/self/maps", "r");
748 	if (fp == NULL)
749 		tst_brk(TBROK | TERRNO, "Failed to open /proc/self/maps.");
750 
751 	while (!feof(fp)) {
752 		unsigned long start, end;
753 		int ret;
754 
755 		ret = fscanf(fp, "%lx-%lx %*[^\n]\n", &start, &end);
756 		if (ret != 2) {
757 			fclose(fp);
758 			tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line.");
759 		}
760 
761 		if ((start >= low) && (start < high)) {
762 			fclose(fp);
763 			return 1;
764 		}
765 		if ((end >= low) && (end < high)) {
766 			fclose(fp);
767 			return 1;
768 		}
769 	}
770 
771 	fclose(fp);
772 	return 0;
773 }
774