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