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