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