ppc/pnv: Add support for POWER8+ LPC Controller
[qemu.git] / tests / migration / stress.c
1 /*
2 * Migration stress workload
3 *
4 * Copyright (c) 2016 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <sys/reboot.h>
27 #include <sys/syscall.h>
28 #include <linux/random.h>
29 #include <sys/time.h>
30 #include <pthread.h>
31 #include <fcntl.h>
32 #include <sys/mount.h>
33 #include <sys/stat.h>
34 #include <sys/mman.h>
35
36 const char *argv0;
37
38 #define PAGE_SIZE 4096
39
40 static int gettid(void)
41 {
42 return syscall(SYS_gettid);
43 }
44
45 static __attribute__((noreturn)) void exit_failure(void)
46 {
47 if (getpid() == 1) {
48 sync();
49 reboot(RB_POWER_OFF);
50 fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n",
51 argv0, gettid(), strerror(errno));
52 abort();
53 } else {
54 exit(1);
55 }
56 }
57
58 static __attribute__((noreturn)) void exit_success(void)
59 {
60 if (getpid() == 1) {
61 sync();
62 reboot(RB_POWER_OFF);
63 fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n",
64 argv0, gettid(), strerror(errno));
65 abort();
66 } else {
67 exit(0);
68 }
69 }
70
71 static int get_command_arg_str(const char *name,
72 char **val)
73 {
74 static char line[1024];
75 FILE *fp = fopen("/proc/cmdline", "r");
76 char *start, *end;
77
78 if (fp == NULL) {
79 fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n",
80 argv0, gettid(), strerror(errno));
81 return -1;
82 }
83
84 if (!fgets(line, sizeof line, fp)) {
85 fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n",
86 argv0, gettid(), strerror(errno));
87 fclose(fp);
88 return -1;
89 }
90 fclose(fp);
91
92 start = strstr(line, name);
93 if (!start)
94 return 0;
95
96 start += strlen(name);
97
98 if (*start != '=') {
99 fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
100 argv0, gettid(), name);
101 }
102 start++;
103
104 end = strstr(start, " ");
105 if (!end)
106 end = strstr(start, "\n");
107
108 if (end == start) {
109 fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
110 argv0, gettid(), name);
111 return -1;
112 }
113
114 if (end)
115 *val = strndup(start, end - start);
116 else
117 *val = strdup(start);
118 return 1;
119 }
120
121
122 static int get_command_arg_ull(const char *name,
123 unsigned long long *val)
124 {
125 char *valstr;
126 char *end;
127
128 int ret = get_command_arg_str(name, &valstr);
129 if (ret <= 0)
130 return ret;
131
132 errno = 0;
133 *val = strtoll(valstr, &end, 10);
134 if (errno || *end) {
135 fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n",
136 argv0, gettid(), name, valstr);
137 free(valstr);
138 return -1;
139 }
140 free(valstr);
141 return 0;
142 }
143
144
145 static int random_bytes(char *buf, size_t len)
146 {
147 int fd;
148
149 fd = open("/dev/urandom", O_RDONLY);
150 if (fd < 0) {
151 fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n",
152 argv0, gettid(), strerror(errno));
153 return -1;
154 }
155
156 if (read(fd, buf, len) != len) {
157 fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n",
158 argv0, gettid(), strerror(errno));
159 close(fd);
160 return -1;
161 }
162
163 close(fd);
164
165 return 0;
166 }
167
168
169 static unsigned long long now(void)
170 {
171 struct timeval tv;
172
173 gettimeofday(&tv, NULL);
174
175 return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull);
176 }
177
178 static int stressone(unsigned long long ramsizeMB)
179 {
180 size_t pagesPerMB = 1024 * 1024 / PAGE_SIZE;
181 char *ram = malloc(ramsizeMB * 1024 * 1024);
182 char *ramptr;
183 size_t i, j, k;
184 char *data = malloc(PAGE_SIZE);
185 char *dataptr;
186 size_t nMB = 0;
187 unsigned long long before, after;
188
189 if (!ram) {
190 fprintf(stderr, "%s (%05d): ERROR: cannot allocate %llu MB of RAM: %s\n",
191 argv0, gettid(), ramsizeMB, strerror(errno));
192 return -1;
193 }
194 if (!data) {
195 fprintf(stderr, "%s (%d): ERROR: cannot allocate %d bytes of RAM: %s\n",
196 argv0, gettid(), PAGE_SIZE, strerror(errno));
197 free(ram);
198 return -1;
199 }
200
201 /* We don't care about initial state, but we do want
202 * to fault it all into RAM, otherwise the first iter
203 * of the loop below will be quite slow. We cna't use
204 * 0x0 as the byte as gcc optimizes that away into a
205 * calloc instead :-) */
206 memset(ram, 0xfe, ramsizeMB * 1024 * 1024);
207
208 if (random_bytes(data, PAGE_SIZE) < 0) {
209 free(ram);
210 free(data);
211 return -1;
212 }
213
214 before = now();
215
216 while (1) {
217
218 ramptr = ram;
219 for (i = 0; i < ramsizeMB; i++, nMB++) {
220 for (j = 0; j < pagesPerMB; j++) {
221 dataptr = data;
222 for (k = 0; k < PAGE_SIZE; k += sizeof(long long)) {
223 ramptr += sizeof(long long);
224 dataptr += sizeof(long long);
225 *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr;
226 }
227 }
228
229 if (nMB == 1024) {
230 after = now();
231 fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n",
232 argv0, gettid(), after, after - before);
233 before = now();
234 nMB = 0;
235 }
236 }
237 }
238
239 free(data);
240 free(ram);
241 }
242
243
244 static void *stressthread(void *arg)
245 {
246 unsigned long long ramsizeMB = *(unsigned long long *)arg;
247
248 stressone(ramsizeMB);
249
250 return NULL;
251 }
252
253 static int stress(unsigned long long ramsizeGB, int ncpus)
254 {
255 size_t i;
256 unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus;
257 ncpus--;
258
259 for (i = 0; i < ncpus; i++) {
260 pthread_t thr;
261 pthread_create(&thr, NULL,
262 stressthread, &ramsizeMB);
263 }
264
265 stressone(ramsizeMB);
266
267 return 0;
268 }
269
270
271 static int mount_misc(const char *fstype, const char *dir)
272 {
273 if (mkdir(dir, 0755) < 0 && errno != EEXIST) {
274 fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n",
275 argv0, gettid(), dir, strerror(errno));
276 return -1;
277 }
278
279 if (mount("none", dir, fstype, 0, NULL) < 0) {
280 fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n",
281 argv0, gettid(), dir, strerror(errno));
282 return -1;
283 }
284
285 return 0;
286 }
287
288 static int mount_all(void)
289 {
290 if (mount_misc("proc", "/proc") < 0 ||
291 mount_misc("sysfs", "/sys") < 0 ||
292 mount_misc("tmpfs", "/dev") < 0)
293 return -1;
294
295 mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9));
296 mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8));
297
298 return 0;
299 }
300
301 int main(int argc, char **argv)
302 {
303 unsigned long long ramsizeGB = 1;
304 char *end;
305 int ch;
306 int opt_ind = 0;
307 const char *sopt = "hr:c:";
308 struct option lopt[] = {
309 { "help", no_argument, NULL, 'h' },
310 { "ramsize", required_argument, NULL, 'r' },
311 { "cpus", required_argument, NULL, 'c' },
312 { NULL, 0, NULL, 0 }
313 };
314 int ret;
315 int ncpus = 0;
316
317 argv0 = argv[0];
318
319 while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
320 switch (ch) {
321 case 'r':
322 errno = 0;
323 ramsizeGB = strtoll(optarg, &end, 10);
324 if (errno != 0 || *end) {
325 fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n",
326 argv0, gettid(), optarg);
327 exit_failure();
328 }
329 break;
330
331 case 'c':
332 errno = 0;
333 ncpus = strtoll(optarg, &end, 10);
334 if (errno != 0 || *end) {
335 fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n",
336 argv0, gettid(), optarg);
337 exit_failure();
338 }
339 break;
340
341 case '?':
342 case 'h':
343 fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0);
344 exit_failure();
345 }
346 }
347
348 if (getpid() == 1) {
349 if (mount_all() < 0)
350 exit_failure();
351
352 ret = get_command_arg_ull("ramsize", &ramsizeGB);
353 if (ret < 0)
354 exit_failure();
355 }
356
357 if (ncpus == 0)
358 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
359
360 fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n",
361 argv0, gettid(), ramsizeGB, ncpus);
362
363 if (stress(ramsizeGB, ncpus) < 0)
364 exit_failure();
365
366 exit_success();
367 }