• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static const struct tst_cgroup_group *cg;
39 
40 volatile int end;
41 static int *nodes;
42 static int nnodes;
43 static long ncpus;
44 
45 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED);
46 static int mem_hog(void);
47 static int mem_hog_cpuset(int ntasks);
48 static long count_cpu(void);
49 
test_cpuset(void)50 static void test_cpuset(void)
51 {
52 	int child, i;
53 	unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 };
54 	char buf[BUFSIZ];
55 
56 	SAFE_CGROUP_READ(cg, "cpuset.cpus", buf, sizeof(buf));
57 	SAFE_CGROUP_PRINT(cg, "cpuset.cpus", buf);
58 	SAFE_CGROUP_READ(cg, "cpuset.mems", buf, sizeof(buf));
59 	SAFE_CGROUP_PRINT(cg, "cpuset.mems", buf);
60 
61 	child = SAFE_FORK();
62 	if (child == 0) {
63 		for (i = 0; i < nnodes; i++) {
64 			if (nodes[i] >= MAXNODES)
65 				continue;
66 			set_node(nmask, nodes[i]);
67 		}
68 		if (set_mempolicy(MPOL_BIND, nmask, MAXNODES) == -1)
69 			tst_brk(TBROK | TERRNO, "set_mempolicy");
70 		exit(mem_hog_cpuset(ncpus > 1 ? ncpus : 1));
71 	}
72 
73 	SAFE_CGROUP_PRINTF(cg, "cpuset.mems", "%d", nodes[0]);
74 	SAFE_CGROUP_PRINTF(cg, "cpuset.mems", "%d", nodes[1]);
75 
76 	tst_reap_children();
77 
78 	tst_res(TPASS, "cpuset test pass");
79 }
80 
setup(void)81 static void setup(void)
82 {
83 	tst_cgroup_require("cpuset", NULL);
84 	ncpus = count_cpu();
85 	if (get_allowed_nodes_arr(NH_MEMS | NH_CPUS, &nnodes, &nodes) < 0)
86 		tst_brk(TBROK | TERRNO, "get_allowed_nodes_arr");
87 	if (nnodes <= 1)
88 		tst_brk(TCONF, "requires a NUMA system.");
89 
90 	cg = tst_cgroup_get_test_group();
91 	SAFE_CGROUP_PRINTF(cg, "cgroup.procs", "%d", getpid());
92 }
93 
cleanup(void)94 static void cleanup(void)
95 {
96 	tst_cgroup_cleanup();
97 }
98 
sighandler(int signo LTP_ATTRIBUTE_UNUSED)99 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED)
100 {
101 	end = 1;
102 }
103 
mem_hog(void)104 static int mem_hog(void)
105 {
106 	long pagesize;
107 	unsigned long *addr;
108 	int ret = 0;
109 
110 	pagesize = getpagesize();
111 	while (!end) {
112 		addr = SAFE_MMAP(NULL, pagesize * 10, PROT_READ | PROT_WRITE,
113 			    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
114 		memset(addr, 0xF7, pagesize * 10);
115 		SAFE_MUNMAP(addr, pagesize * 10);
116 	}
117 	return ret;
118 }
119 
mem_hog_cpuset(int ntasks)120 static int mem_hog_cpuset(int ntasks)
121 {
122 	int i, status, ret = 0;
123 	struct sigaction sa;
124 	pid_t *pids;
125 
126 	if (ntasks <= 0)
127 		tst_brk(TBROK | TERRNO, "ntasks is small.");
128 	sa.sa_handler = sighandler;
129 	if (sigemptyset(&sa.sa_mask) < 0)
130 		tst_brk(TBROK | TERRNO, "sigemptyset");
131 	sa.sa_flags = 0;
132 	if (sigaction(SIGUSR1, &sa, NULL) < 0)
133 		tst_brk(TBROK | TERRNO, "sigaction");
134 
135 	pids = SAFE_MALLOC(sizeof(pid_t) * ntasks);
136 	for (i = 0; i < ntasks; i++) {
137 		switch (pids[i] = fork()) {
138 		case -1:
139 			tst_res(TFAIL | TERRNO, "fork %d", pids[i]);
140 			ret = 1;
141 			break;
142 		case 0:
143 			ret = mem_hog();
144 			exit(ret);
145 		default:
146 			break;
147 		}
148 	}
149 
150 	while (i--) {
151 		if (kill(pids[i], SIGUSR1) == -1) {
152 			tst_res(TFAIL | TERRNO, "kill %d", pids[i]);
153 			ret = 1;
154 		}
155 	}
156 	while (waitpid(-1, &status, WUNTRACED | WCONTINUED) > 0) {
157 		if (WIFEXITED(status)) {
158 			if (WEXITSTATUS(status) != 0) {
159 				tst_res(TFAIL, "child exit status is %d",
160 					 WEXITSTATUS(status));
161 				ret = 1;
162 			}
163 		} else if (WIFSIGNALED(status)) {
164 			tst_res(TFAIL, "child caught signal %d",
165 				 WTERMSIG(status));
166 			ret = 1;
167 		}
168 	}
169 	return ret;
170 }
171 
count_cpu(void)172 static long count_cpu(void)
173 {
174 	int ncpus = 0;
175 
176 	while (path_exist(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus))
177 		ncpus++;
178 
179 	return ncpus;
180 }
181 
182 static struct tst_test test = {
183 	.needs_root = 1,
184 	.forks_child = 1,
185 	.setup = setup,
186 	.cleanup = cleanup,
187 	.test_all = test_cpuset,
188 	.min_kver = "2.6.32",
189 };
190 
191 #else
192 	TST_TEST_TCONF(NUMA_ERROR_MSG);
193 #endif
194