xref: /aosp_15_r20/external/ltp/testcases/kernel/mem/cpuset/cpuset01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 /*
2  * Copyright (C) 2010-2017  Red Hat, Inc.
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  *
14  * Out Of Memory when changing cpuset's mems on NUMA. There was a
15  * problem reported upstream that the allocator may see an empty
16  * nodemask when changing cpuset's mems.
17  * http://lkml.org/lkml/2010/5/4/77
18  * http://lkml.org/lkml/2010/5/4/79
19  * http://lkml.org/lkml/2010/5/4/80
20  * This test is based on the reproducers for the above issue.
21  */
22 
23 #include "config.h"
24 #include <stdio.h>
25 #include <sys/wait.h>
26 #if HAVE_NUMA_H
27 #include <numa.h>
28 #endif
29 #if HAVE_NUMAIF_H
30 #include <numaif.h>
31 #endif
32 
33 #include "mem.h"
34 #include "numa_helper.h"
35 
36 #ifdef HAVE_NUMA_V2
37 
38 volatile int end;
39 static int *nodes;
40 static int nnodes;
41 static long ncpus;
42 
43 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED);
44 static int mem_hog(void);
45 static int mem_hog_cpuset(int ntasks);
46 static long count_cpu(void);
47 
test_cpuset(void)48 static void test_cpuset(void)
49 {
50 	int child, i;
51 	unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 };
52 	char buf[BUFSIZ];
53 
54 	SAFE_CG_READ(tst_cg, "cpuset.cpus", buf, sizeof(buf));
55 	SAFE_CG_PRINT(tst_cg, "cpuset.cpus", buf);
56 	SAFE_CG_READ(tst_cg, "cpuset.mems", buf, sizeof(buf));
57 	SAFE_CG_PRINT(tst_cg, "cpuset.mems", buf);
58 
59 	child = SAFE_FORK();
60 	if (child == 0) {
61 		for (i = 0; i < nnodes; i++) {
62 			if (nodes[i] >= MAXNODES)
63 				continue;
64 			set_node(nmask, nodes[i]);
65 		}
66 		if (set_mempolicy(MPOL_BIND, nmask, MAXNODES) == -1)
67 			tst_brk(TBROK | TERRNO, "set_mempolicy");
68 		exit(mem_hog_cpuset(ncpus > 1 ? ncpus : 1));
69 	}
70 
71 	SAFE_CG_PRINTF(tst_cg, "cpuset.mems", "%d", nodes[0]);
72 	SAFE_CG_PRINTF(tst_cg, "cpuset.mems", "%d", nodes[1]);
73 
74 	tst_reap_children();
75 
76 	tst_res(TPASS, "cpuset test pass");
77 }
78 
setup(void)79 static void setup(void)
80 {
81 	ncpus = count_cpu();
82 	if (get_allowed_nodes_arr(NH_MEMS | NH_CPUS, &nnodes, &nodes) < 0)
83 		tst_brk(TBROK | TERRNO, "get_allowed_nodes_arr");
84 	if (nnodes <= 1)
85 		tst_brk(TCONF, "requires a NUMA system.");
86 
87 	SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid());
88 }
89 
sighandler(int signo LTP_ATTRIBUTE_UNUSED)90 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED)
91 {
92 	end = 1;
93 }
94 
mem_hog(void)95 static int mem_hog(void)
96 {
97 	long pagesize;
98 	unsigned long *addr;
99 	int ret = 0;
100 
101 	pagesize = getpagesize();
102 	while (!end) {
103 		addr = SAFE_MMAP(NULL, pagesize * 10, PROT_READ | PROT_WRITE,
104 			    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
105 		memset(addr, 0xF7, pagesize * 10);
106 		SAFE_MUNMAP(addr, pagesize * 10);
107 	}
108 	return ret;
109 }
110 
mem_hog_cpuset(int ntasks)111 static int mem_hog_cpuset(int ntasks)
112 {
113 	int i, status, ret = 0;
114 	struct sigaction sa;
115 	pid_t *pids;
116 
117 	if (ntasks <= 0)
118 		tst_brk(TBROK | TERRNO, "ntasks is small.");
119 	sa.sa_handler = sighandler;
120 	if (sigemptyset(&sa.sa_mask) < 0)
121 		tst_brk(TBROK | TERRNO, "sigemptyset");
122 	sa.sa_flags = 0;
123 	if (sigaction(SIGUSR1, &sa, NULL) < 0)
124 		tst_brk(TBROK | TERRNO, "sigaction");
125 
126 	pids = SAFE_MALLOC(sizeof(pid_t) * ntasks);
127 	for (i = 0; i < ntasks; i++) {
128 		switch (pids[i] = fork()) {
129 		case -1:
130 			tst_res(TFAIL | TERRNO, "fork %d", pids[i]);
131 			ret = 1;
132 			break;
133 		case 0:
134 			ret = mem_hog();
135 			exit(ret);
136 		default:
137 			break;
138 		}
139 	}
140 
141 	while (i--) {
142 		if (kill(pids[i], SIGUSR1) == -1) {
143 			tst_res(TFAIL | TERRNO, "kill %d", pids[i]);
144 			ret = 1;
145 		}
146 	}
147 	while (waitpid(-1, &status, WUNTRACED | WCONTINUED) > 0) {
148 		if (WIFEXITED(status)) {
149 			if (WEXITSTATUS(status) != 0) {
150 				tst_res(TFAIL, "child exit status is %d",
151 					 WEXITSTATUS(status));
152 				ret = 1;
153 			}
154 		} else if (WIFSIGNALED(status)) {
155 			tst_res(TFAIL, "child caught signal %d",
156 				 WTERMSIG(status));
157 			ret = 1;
158 		}
159 	}
160 	return ret;
161 }
162 
count_cpu(void)163 static long count_cpu(void)
164 {
165 	int ncpus = 0;
166 
167 	while (path_exist(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus))
168 		ncpus++;
169 
170 	return ncpus;
171 }
172 
173 static struct tst_test test = {
174 	.needs_root = 1,
175 	.forks_child = 1,
176 	.setup = setup,
177 	.test_all = test_cpuset,
178 	.needs_cgroup_ctrls = (const char *const []){ "cpuset", NULL },
179 };
180 
181 #else
182 	TST_TEST_TCONF(NUMA_ERROR_MSG);
183 #endif
184